Java Spring无法处理“;多部分/表格数据”;POST请求(错误400“;请求错误”;)

Java Spring无法处理“;多部分/表格数据”;POST请求(错误400“;请求错误”;),java,spring,Java,Spring,当我将请求发布到此服务器代码时-一切正常: @RequestMapping(method = RequestMethod.POST, consumes = { "application/json" }) public ResponseEntity<String> addQuestion(@RequestBody String dtoObject) { ... } @RequestMapping(method=RequestMethod.POST,使用={“application/j

当我将请求发布到此服务器代码时-一切正常:

@RequestMapping(method = RequestMethod.POST, consumes = { "application/json" })
public ResponseEntity<String> addQuestion(@RequestBody String dtoObject) { ... }
@RequestMapping(method=RequestMethod.POST,使用={“application/json”})
public ResponseEntity addQuestion(@RequestBody String dtoObject){…}
但如果我将请求更改为“multipart/form data”-Spring返回错误400“Bad request”:

@RequestMapping(method=RequestMethod.POST,使用={“多部分/表单数据”})
public ResponseEntity addQuestion(@RequestBody String dtoObject){…}
为什么?? 也许我应该多做些豆子


PS:我需要“多部分/表单数据”来与json对象一起发送文件。

我认为您无法将文件反序列化到请求体中的dtoObject。您需要使用@RequestPart来完成这项工作

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<String> addQuestion2(@RequestPart("question") QuestionPostDto dtoObject, @RequestPart("file") MultiPartFile file)  { ... }
或者,如果不想传递json格式的文件,可以使用普通字符串传递

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
    public ResponseEntity<String> addQuestion2(String question, @RequestPart("file") MultiPartFile file)  {
    QuestionPostDto dtoObject = new ObjectMapper().readValue(request, QuestionPostDto.class); 
    // do sth
}

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="QLbLFIR.gif"
Content-Type: image/gif


------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="request"

{
    "key": "value"
}
------WebKitFormBoundary7MA4YWxkTrZu0gW--
@RequestMapping(method=RequestMethod.POST,使用={“多部分/表单数据”})
public ResponseEntity addQuestion2(字符串问题,@RequestPart(“文件”)多部分文件){
QuestionPostDto dtoObject=new ObjectMapper().readValue(请求,QuestionPostDto.class);
//做某事
}
内容类型:多部分/表单数据;边界=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
内容配置:表单数据;name=“file”;filename=“QLbLFIR.gif”
内容类型:图像/gif
------WebKitFormBoundary7MA4YWxkTrZu0gW
内容配置:表单数据;name=“请求”
{
“键”:“值”
}
------WebKitFormBoundary7MA4YWxkTrZu0gW--
有关详细信息,请参阅此线程:

我认为您无法将文件反序列化到请求正文中的dtoObject。您需要使用@RequestPart来完成这项工作

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<String> addQuestion2(@RequestPart("question") QuestionPostDto dtoObject, @RequestPart("file") MultiPartFile file)  { ... }
或者,如果不想传递json格式的文件,可以使用普通字符串传递

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
    public ResponseEntity<String> addQuestion2(String question, @RequestPart("file") MultiPartFile file)  {
    QuestionPostDto dtoObject = new ObjectMapper().readValue(request, QuestionPostDto.class); 
    // do sth
}

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="QLbLFIR.gif"
Content-Type: image/gif


------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="request"

{
    "key": "value"
}
------WebKitFormBoundary7MA4YWxkTrZu0gW--
@RequestMapping(method=RequestMethod.POST,使用={“多部分/表单数据”})
public ResponseEntity addQuestion2(字符串问题,@RequestPart(“文件”)多部分文件){
QuestionPostDto dtoObject=new ObjectMapper().readValue(请求,QuestionPostDto.class);
//做某事
}
内容类型:多部分/表单数据;边界=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
内容配置:表单数据;name=“file”;filename=“QLbLFIR.gif”
内容类型:图像/gif
------WebKitFormBoundary7MA4YWxkTrZu0gW
内容配置:表单数据;name=“请求”
{
“键”:“值”
}
------WebKitFormBoundary7MA4YWxkTrZu0gW--
有关详细信息,请参阅此线程: 我解决了这个问题

1) 我创建了HttpMessageConverter,它将json转换为我的自定义类型QuestionPostDto:

public class QuestionPostDtoHttpMessageConverter implements HttpMessageConverter<QuestionPostDto> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return QuestionPostDto.class == clazz;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return QuestionPostDto.class == clazz;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.MULTIPART_FORM_DATA);
        return list;
    }

    @Override
    public QuestionPostDto read(Class<? extends QuestionPostDto> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        InputStream istream = inputMessage.getBody();
        String requestString = IOUtils.toString(istream, "UTF-8");

        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(requestString, QuestionPostDto.class);
    }

    @Override
    public void write(QuestionPostDto t, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
    }
}
3) 现在,我在RestController中的代码运行良好:

@RestController
@RequestMapping("/api/question")
@ConfigurationProperties(prefix = "question")
@RequiredArgsConstructor
@Slf4j
public class QuestionController {
...
@PostMapping
ResponseEntity<String> addQuestion(@RequestPart("dtoObject") QuestionPostDto dtoObject, @RequestPart("file") MultipartFile file) { ... }
@RestController
@请求映射(“/api/question”)
@配置属性(前缀=“问题”)
@所需参数构造函数
@Slf4j
公共类问题控制器{
...
@邮戳
ResponseEntity addQuestion(@RequestPart(“dtoObject”)QuestionPostDto dtoObject,@RequestPart(“file”)MultipartFile文件){…}
感谢大家,特别是我解决了这个问题

1) 我创建了HttpMessageConverter,它将json转换为我的自定义类型QuestionPostDto:

public class QuestionPostDtoHttpMessageConverter implements HttpMessageConverter<QuestionPostDto> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return QuestionPostDto.class == clazz;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return QuestionPostDto.class == clazz;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.MULTIPART_FORM_DATA);
        return list;
    }

    @Override
    public QuestionPostDto read(Class<? extends QuestionPostDto> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        InputStream istream = inputMessage.getBody();
        String requestString = IOUtils.toString(istream, "UTF-8");

        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(requestString, QuestionPostDto.class);
    }

    @Override
    public void write(QuestionPostDto t, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
    }
}
3) 现在,我在RestController中的代码运行良好:

@RestController
@RequestMapping("/api/question")
@ConfigurationProperties(prefix = "question")
@RequiredArgsConstructor
@Slf4j
public class QuestionController {
...
@PostMapping
ResponseEntity<String> addQuestion(@RequestPart("dtoObject") QuestionPostDto dtoObject, @RequestPart("file") MultipartFile file) { ... }
@RestController
@请求映射(“/api/question”)
@配置属性(前缀=“问题”)
@所需参数构造函数
@Slf4j
公共类问题控制器{
...
@邮戳
ResponseEntity addQuestion(@RequestPart(“dtoObject”)QuestionPostDto dtoObject,@RequestPart(“file”)MultipartFile文件){…}

感谢大家,特别是如果您使用Postman之类的工具来测试REST服务路线,那么在该工具中,您必须将
内容类型
标题键分配为
多部分/表单数据
,在Postman中,我会得到相同的结果(错误400“错误请求”)请参考您的控制台并阅读此异常的整个堆栈跟踪,在那里您可能知道此错误的来源。mustabelMo:抱歉,可能是我不理解您:错误400“错误请求”不是异常,它是服务器响应的代码,因此我没有stacktrace。通常当发生异常时,我们可以在IDE控制台上看到stacktrace的日志。如果您使用Postman之类的工具来测试REST服务路由,则在该工具中,您必须将
内容类型
头键分配为
multipart/form data
在Postman中,我得到了相同的结果(错误400“错误请求”),请参考您的控制台并阅读此异常的整个堆栈跟踪,您可能知道此错误的来源。mustabelMo:抱歉,可能是我不明白您的意思:错误400“错误请求”不是异常,它是服务器响应的代码,因此我没有它的stacktrace。通常当发生异常时,我们可以在IDE控制台上看到stacktrace的日志如果我尝试发布字符串-我得到错误400“错误请求”(编辑问题).@RequestPart-可用于字符串,但不适用于我的自定义类型QuestionPostDto(Spring返回错误415“不支持的媒体类型”)。您是否使用postman测试您的请求,如果是,我可以查看您的请求负载吗?Chi Dov,谢谢您提供的信息。您是对的-我必须使用@RequestPart。请参见下面的答案。如果我尝试发布字符串-我会收到错误400“错误请求”(编辑了问题)。@RequestPart-使用字符串,但不使用我的自定义类型QuestionPostDto(Spring返回错误415“不支持的媒体类型”)。您是否正在使用postman测试您的请求,如果是,我可以查看您的请求负载吗?Chi Dov,感谢您提供的信息。您是对的-我必须使用@RequestPart。请参阅下面的回答。