We are trying to upgrade to 1.3.0.M1 from 1.2.1. When using the 1.2.1 version, we did not have to set the Content-Type: application/json header when sending a request body to a method web service endpoint like:
@RequestMapping(value = "filter", method = RequestMethod.POST)
List<StateData> filter(@RequestBody StateData filter) {
}
The Content-Type would be With version 1.3.0.M1, the call will fail when passing a @RequestBody if the Content-Type header is not set, with the error:
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported at
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:216) at
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:148) at
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:126) at
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77) at
org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162) at
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129) at
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111) at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:799) at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:728) at
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
... 67 more
Comment From: bclozel
Hi @lewisdavidcole
Could you give more details about this issue please? Especially, HTTP headers for request and response. In any case, what you're describing here looks normal - application/octet-stream
is a sensible default for "Content-Type"
if none is sent by the client. What HTTP client are you using?
I've just reproduced this exact behavior in Spring Boot 1.2.1, with the following:
@RestController
public class TestController {
@RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(@RequestBody Person person) {
return "Hello " + person.getName();
}
private static class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
curl --data '{"name":"test"}' -v -X POST -H 'Content-Type:' http://localhost:8080/test
> POST /test HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:8080
> Accept: */*
> Content-Length: 15
< HTTP/1.1 415 Unsupported Media Type
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Wed, 24 Jun 2015 09:20:22 GMT
<
{"timestamp":1435137622778,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/octet-stream' not supported","path":"/test"}%
curl --data '{"name":"test"}' -v -X POST -H 'Content-Type:application/json' http://localhost:8080/test
> POST /test HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:8080
> Accept: */*
> Content-Type:application/json
> Content-Length: 15
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 10
< Date: Wed, 24 Jun 2015 09:23:46 GMT
<
Hello test%
Maybe do you have a custom ContentNegotiationStrategy
that sets the defaultContentType
? I don't think Boot is doing anything like this.
Comment From: lewisdavidcole
Hi Phil, thanks for responding.
I totally agree that application/octet-stream is an acceptable default if the Content-Type header is not set. That makes total sense. I was just thrown off as to why simply changing the version in my gradle.build from 1.2.1 to 1.3.0M1 altered the behavior I was seeing.
Digging a little deeper, I found that the issue is specifically only when making a POST request where I don't actually set anything on the request body. It was a little clouded by testing in PostMan and not curl. In PostMan, when I don't have the Content-Type set, use a POST, and don't add data to the request, it's successful in 1.2.1. It fails in 1.3.0M1. I can get it to work in Curl, but I have to remove the --data flag all together to match the behavior. If i have an empty --data '' in curl, the error is given.
So narrowing in on the findings: 1.3.0M1 fails when you don't add data to a POST request, but 1.2.1 does not.
I'm OK with the requirement to set the Content-Type, that's sensible. However, I think it worthwhile to note that the behavior is slightly different in 1.3.0M1 compared to earlier versions for this one use case.
Comment From: rstoyanchev
We did make some changes in this area https://github.com/spring-projects/spring-framework/commit/36ed4df59df03082b510841e55511d02424b7e56#diff-1b0f5771c87599a9c49da7e88ddb788b related to the introduction of RequestBodyAdvice as well as https://jira.spring.io/browse/SPR-12778.
Given that the method declares a required @RequestBody if the body is empty, it makes sense to have the exception. Can you describe exactly what was the before and after behavior? If possible provide a snippet of code how to reproduce the behavior.
Comment From: bclozel
Even if you don't manually set a Content-Type
, Postman will set one for you anyway.
Here is an example (look at the bottom of the screen capture, Content-Type: text/plain
.
Too bad Postman only shows headers received, and not the actual headers sent.
Now I did try to reproduce your use case.
Spring Boot 1.2.1, same Controller, POST request with empty body sent by Postman:
{
"timestamp": 1435441055428,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Required request body content is missing: org.springframework.web.method.HandlerMethod$HandlerMethodParameter@ca37b95",
"path": "/test"
}
In that case, it's complaining because the request body is missing and our controller is expecting one.
Same thing with Spring Boot 1.3.0.M1, :
{
"timestamp": 1435441392535,
"status": 415,
"error": "Unsupported Media Type",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
"message": "Content type 'text/plain;charset=UTF-8' not supported",
"path": "/test"
}
It's saying our controller does not support this Content-Type
, which is true.
So there is a change of behavior here, probably coming from Spring Framework itself.
I suspect this comes from RequestResponseBodyMethodProcessor
but I have yet to confirm that.
In any case, I couldn't reproduce the "working" state.
Comment From: lewisdavidcole
Thanks for looking deeper into the behavior. I think there is one aspect missing: The input to the controller method is marked as optional:
@RestController
@RequestMapping(value = "sample")
class SampleController {
@RequestMapping(value = "filter", method = [RequestMethod.GET, RequestMethod.POST])
SampleData filter(@RequestBody(required = false) SampleData filter) {
filter ?: new SampleData()
}
}
There are 2 important parts to the method signature: 1) It supports both GET and POST 2) the @RequestBody(required=false) makes the data optional
There is nothing special about the SampleData object, but note that all my code is in Groovy:
class SampleData {
String sampleString1 = "default"
String sampleString2 = "default"
}
I created these in a separate sample project to remove any inadvertent configuration or code that would affect the outcome. If you are interested, here is my build.gradle file (using 3.0.0M1):
group 'com.rbs'
version '1.0-SNAPSHOT'
task wrapper(type: Wrapper) {
gradleVersion = '2.4'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
buildscript {
repositories {
jcenter()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.3.0.M1"
}
}
repositories {
jcenter()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
}
apply plugin: "spring-boot"
apply plugin: "idea"
apply plugin: "groovy"
sourceCompatibility = 1.8
dependencies {
compile 'org.codehaus.groovy:groovy-all',
'org.springframework.boot:spring-boot-starter-web'
}
springBoot { mainClass = "com.rbs.TestApp" }
Using the sample project and only cURL instead of PostMan, you ca see the results and URL's used at the bottom of the post.
Summary
The results are pretty clear, there is an obvious change in behavior, which I think has degraded.
Version 1.2.1
GET requests don't accept a response body, so the Content-Type is never required. POST requests - ONLY if data is set on the request body is the Content-Type needed.
Version 3.0.0M1
GET requests where the controller method has an optional @RequestBody requires the Content-Type to be set, whether or not the data is set on the request body. Note that in the example Controller method, the method supports both GET and POST requests, with the @RequestBody's required attribute set false.
POST requests - The Content-Type is always required whether the request body is set or not.
I believe the way version 1.2.1 functions is the correct behavior. Because the @RequestBody can be an optional parameter, the Content-Type should not be required if the request body is not set, regardless of the request type.
Just for fyi... we have the filter endpoint to support both GET and POST requests because we allow the filter to be called without criteria as a GET to retrieve all results possible.
cURL Results
Spring Boot 1.3.0.M1
GET request with Content-Type set
curl -X GET -H "Content-Type: application/json" http://localhost:8080/TestApp/sample/filter
Connected to localhost (127.0.0.1) port 8080 (#0) GET /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Type: application/json
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < X-Application-Context: application:8080 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 18:49:33 GMT < {"sampleString1":"default","sampleString2":"default"}* Connection #0 to host localhost left intact
GET request with no Content-Type set
curl -v -X GET http://localhost:8080/TestApp/sample/filter
GET /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: /
< HTTP/1.1 415 Unsupported Media Type < Server: Apache-Coyote/1.1 < X-Application-Context: application:8080 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:00:38 GMT < {"timestamp":"2015-06-29T20:00:38.582+0000","status":415,"error":"Unsupported Media Type","exception ":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/ octet-stream' not supported","path":"/TestApp/sample/filter"}* Connection #0 to host localhost left intact
POST with no Content-Type and no request body
curl -v -X POST http://localhost:8080/TestApp/sample/filter
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: /
< HTTP/1.1 415 Unsupported Media Type < Server: Apache-Coyote/1.1 < X-Application-Context: application:8080 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 18:55:39 GMT < {"timestamp":"2015-06-29T18:55:39.344+0000","status":415,"error":"Unsupported Media Type","exception ":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/ octet-stream' not supported","path":"/TestApp/sample/filter"}* Connection #0 to host localhost left intact
POST with no Content-Type and a request body
curl --data "{\"sampleString1\":\"test\"}" -v -X POST "http://localhost:8080/TestApp/sample/filter
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Length: 24 Content-Type: application/x-www-form-urlencoded
upload completely sent off: 24 out of 24 bytes < HTTP/1.1 415 Unsupported Media Type < Server: Apache-Coyote/1.1 < X-Application-Context: application:8080 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:13:27 GMT < {"timestamp":"2015-06-29T20:13:27.388+0000","status":415,"error":"Unsupported Media Type","exception ":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/ x-www-form-urlencoded;charset=UTF-8' not supported","path":"/TestApp/sample/filter"}* Connection #0 to host localhost left intact
POST with Content-Type set and no request body
curl -v -X POST -H "Content-Type: application/json" http://localhost:8080/TestApp/sample/filter
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Type: application/json
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < X-Application-Context: application:8080 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 18:56:27 GMT < {"sampleString1":"default","sampleString2":"default"}* Connection #0 to host localhost left intact
POST with Content-Type set and a request body
curl --data "{\"sampleString1\":\"test\"}" -v -X POST -H "Content-Type: application/json" "http://localhost:8080/TestApp/sample/filter"
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Type: application/json Content-Length: 24
< upload completely sent off: 24 out of 24 bytes < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < X-Application-Context: application:8080 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 19:57:45 GMT < {"sampleString1":"test","sampleString2":"default"}* Connection #0 to host localhost left intact
Spring Boot 1.2.1
GET request with Content-Type set
curl -v -X GET -H "Content-Type: application/json" http://localhost:8080/TestApp/sample/filter
GET /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Type: application/json
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:20:49 GMT < {"sampleString1":"default","sampleString2":"default"}* Connection #0 to host localhost left intact
GET request with no Content-Type set
curl -v -X GET http://localhost:8080/TestApp/sample/filter
GET /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: /
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:21:23 GMT < {"sampleString1":"default","sampleString2":"default"}* Connection #0 to host localhost left intact
POST with no Content-Type and no request body
curl -v -X POST http://localhost:8080/TestApp/sample/filter
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: /
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:24:14 GMT < {"sampleString1":"default","sampleString2":"default"}* Connection #0 to host localhost left intact
POST with no Content-Type and a request body
curl --data "{\"sampleString1\":\"test\"}" -v -X POST "http://localhost:8080/TestApp/sample/filter"
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Length: 24 Content-Type: application/x-www-form-urlencoded - upload completely sent off: 24 out of 24 bytes < HTTP/1.1 415 Unsupported Media Type < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:25:35 GMT < {"timestamp":"2015-06-29T20:25:35.601+0000","status":415,"error":"Unsupported Media Type","exception ":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/ x-www-form-urlencoded;charset=UTF-8' not supported","path":"/TestApp/sample/filter"}* Connection #0 to host localhost left intact
POST with Content-Type set and no request body
curl -v -X POST -H "Content-Type: application/json" http://localhost:8080/TestApp/sample/filter
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Type: application/json
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:22:23 GMT < {"sampleString1":"default","sampleString2":"default"}* Connection #0 to host localhost left intact
POST with Content-Type set and a request body
curl --data "{\"sampleString1\":\"test\"}" -v -X POST -H "Content-Type: application/json" "http://localhost:8080/TestApp/sample/filter"
POST /TestApp/sample/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: / Content-Type: application/json Content-Length: 24
upload completely sent off: 24 out of 24 bytes < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 20:26:55 GMT < {"sampleString1":"test","sampleString2":"default"}* Connection #0 to host localhost left intact
Comment From: rstoyanchev
Hm, I'm unable to reproduce this behavior. I used start.spring.io, check web. Then modified DemoApplication:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RestController
@RequestMapping(value = "sample")
public static class SampleController {
@RequestMapping(value = "filter", method = {RequestMethod.GET, RequestMethod.POST})
public String filter(@RequestBody(required = false) String filter) {
return filter != null ? filter : "no input";
}
}
}
Modified the pom.xml to use Boot 1.3.0.M1 + added milestone repo. What am I missing?
Comment From: lewisdavidcole
Apparently, it is the fact that you are using a simple String as the filter parameter type and not a complex Object. There is a code snippet above of the data object I used. When I use your method, it works, mine doesn't:
@RequestMapping(value = "filter", method = [RequestMethod.GET, RequestMethod.POST])
SampleData filter(@RequestBody(required = false) SampleData filter) {
filter ?: new SampleData()
}
@RequestMapping(value = "filter-string", method = [RequestMethod.GET, RequestMethod.POST])
public String filter(@RequestBody(required = false) String filter) {
return filter != null ? filter : "no input";
}
curl -v -X POST http://localhost:8080/TestApp/sample-controller/filter
POST /TestApp/sample-controller/filter HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: /
< HTTP/1.1 415 Unsupported Media Type < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Mon, 29 Jun 2015 22:35:29 GMT < {"timestamp":"2015-06-29T22:35:29.183+0000","status":415,"error":"Unsupported Media Type","exception ":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/ octet-stream' not supported","path":"/TestApp/sample-controller/filter"}* Connection #0 to host loca lhost left intact
curl -v -X POST http://localhost:8080/TestApp/sample-controller/filter-string
POST /TestApp/sample-controller/filter-string HTTP/1.1 User-Agent: curl/7.39.0 Host: localhost:8080 Accept: /
< HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: text/plain;charset=UTF-8 < Content-Length: 8 < Date: Mon, 29 Jun 2015 22:35:46 GMT < no input* Connection #0 to host localhost left intact
Comment From: rstoyanchev
Okay, thanks I can reproduce it now.
Comment From: bclozel
Thanks a lot @lewisdavidcole for all your work. This is indeed a subtle issue to reproduce.
We've tracked it down to the right class and we've created an issue: SPR-13176. The fix will be probably included in the next Boot milestone.
In the meantime, just an opinion: using required=false
to use a controller handler for GET and POST requests is a bit of a workaround. Something like this makes it easier to understand your application, appears as separate handlers in the list of mapped handlers with the Boot actuator feature, and is less expensive since args processing does not happen anymore for GET requests:
@RequestMapping("filter")
public SampleData getFilter() {
return addFilter(null);
}
@RequestMapping(value = "filter", method = RequestMethod.POST)
public SampleData filter(@RequestBody(required = false) SampleData filter) {
//...
}
Comment From: lewisdavidcole
@bclozel I totally agree with your comment of having a single method supporting two request types.. accidental complexity (or intentional :) ).
Thanks for working through the issue to reproduce and identify a solution. Spring Boot rocks and has a great dev team!
Hope to see you at Uber Conf in Denver this July!
Comment From: sbrannen
I'm quite aware that I'm being pedantic here, but....
Can we please start using the path
attribute alias for value
in our code and examples?
@RequestMapping(path = "filter", method = RequestMethod.POST)
:smirk:
Comment From: marveshy
for my case the headers must be :
Content-Type: application/json and i solve the problem good luck
Comment From: cagataygurturk
Hello, for me the problem persists for Spring boot 1.4.1 and Spring MVC 4.3.3.
I am trying to consume AWS SNS messages (which are interestingly sent with Content-Type: text/plain; charset=UTF-8 headers). My controller is like the following:
@PostMapping(value = Routes.NOTIFICATION_HANDLER_ROUTE)
public String notificationHandler(@RequestBody(required = false) SNSMessage snsMessage) {
// Process SNSMessage object
return "test";
}
When I send request with Content-Type: application/json
header manually and with JSON String, SNSMessage is serialized successfully by Jackson. When I change the header to text/plain, I get org.springframework.web.HttpMediaTypeNotSupportedException
exception.
From HTTP standarts point of view it is true, the response should be 415 because I send JSON instead of plain text but as I am not able to change the Content-Type header of AWS, so there should be an option to disable the Content-Type header check.
Comment From: wilkinsona
@cagataygurturk That's something that would have to be addressed in Spring MVC rather than in Spring Boot. Without any changes in Spring MVC, one thing that you could do is to register an HTTP message converter that treats text/plain
as JSON.
Comment From: rstoyanchev
It's not a Content-Type header check but rather a failure to find a suitable HttpMessageConverter
. The StringHttpMessageConverter
can deal with "text/plain" but it only converts to String. The JacksonHttpMessageConverter
can convert to SNSMessage
but it only gets involved for application/json
. You could configure the JacksonHttpMessageConverter
to also convert text/plain
but that could cause you issues in other endpoints. I do not see what we could do in Spring MVC to make this better. We are in a worse position to make assumptions.
Probably the simplest reliable thing to do is to insert the request body as a String and invoke the Jackson converter manually. This is just a weird situation to begin with. Another option might be to use a custom ContentNegotiationStrategy
that resolves the content type for very specific URLs. Then you can keep your method signature as it is.
Comment From: cagataygurturk
@wilkinsona For me it is also an Spring MVC issue but as I saw the problem is being discussed here, I decided to post here.
@rstoyanchev You are right. A generic HttpMessageConverter could cause issues in other controllers because I want to do only one exception for one single controller. Well, for now I am deserialising it manually.
If you are interested about why AWS is sending text/plain Content-Type you can see this thread: https://forums.aws.amazon.com/thread.jspa?threadID=69413
Very interesting response from an AWS employee:
Very simply, it was an oversight on our part. It should have been 'application/json' all along but we missed it. Now we have to find a way to change it to what it should be without breaking anyone's integration with SNS.
Comment From: ztgoto
spring boot version: 1.4.1.RELEASE
@RequestMapping(value="/testPara",method=RequestMethod.PUT,consumes="application/x-www-form-urlencoded")
@ResponseStatus(value = HttpStatus.OK)
public void testPara(@RequestBody MultiValueMap<String, String> values){
System.out.println(values);
}
response
{
"timestamp": 1482319313297,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Required request body is missing: public void com.raxtone.lbs.controllers.TestController.testPara(org.springframework.util.MultiValueMap<java.lang.String, java.lang.String>)",
"path": "/test/testPara"
}
class
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver
method:readWithMessageConverters
parameter:HttpInputMessage inputMessage=org.springframework.http.server.ServletServerHttpRequest
in property:servletRequest=org.springframework.web.filter.HttpPutFormContentFilter$HttpPutFormContentRequestWrapper
inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
getBody=null
public EmptyBodyCheckingHttpInputMessage(HttpInputMessage inputMessage) throws IOException {
this.headers = inputMessage.getHeaders();
InputStream inputStream = inputMessage.getBody();
if (inputStream == null) {
this.body = null;
}
else if (inputStream.markSupported()) {
inputStream.mark(1);
this.body = (inputStream.read() != -1 ? inputStream : null);
inputStream.reset();
}
else {
PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
int b = pushbackInputStream.read();
if (b == -1) {
this.body = null;
}
else {
this.body = pushbackInputStream;
pushbackInputStream.unread(b);
}
}
this.method = ((HttpRequest) inputMessage).getMethod();
}
pushbackInputStream.read()=-1
Comment From: philwebb
@ztgoto This issue has been closed for a while. If you think you've found a new bug please open a fresh issue and provide a sample application that shows it.
Comment From: user20161119
I got same issue,I use resttemplate request spring boot client as follow:
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>(); headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); boolean hasMore = true; while (hasMore) { paginationInput.setEntriesPerPage(2); paginationInput.setPageNumber(1); HttpEntity<FindCompletedItemsReq> request = new HttpEntity<FindCompletedItemsReq>(finding_req,headers); ResponseEntity<String> resp_entity = restTemplate.exchange(url,HttpMethod.POST, request, String.class); ``` and the http client log show the Content-Type header is correct:
-
http-outgoing-1 >> "Accept: application/json[\r][\n]" 2017-09-18 22:27:42.621 DEBUG 4176 --- [p-nio-80-exec-9] o.a.h.wire : http-outgoing-1 >> "Content-Type: application/json[\r][\n]" 2017-09-18 22:27:42.622 DEBUG 4176 --- [p-nio-80-exec-9] o.a.h.wire : http-outgoing-1 >> "Content-Length: 737[\r][\n]" 2017-09-18 22:27:42.622 DEBUG 4176 --- [p-nio-80-exec-9] o.a.h.wire
and response the exception
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported
``
I am not sure why the Content-Type header change to
application/json;charset=UTF-8`
Comment From: wilkinsona
@user20161119 This issue has been closed for a while. If you think you've found a new bug please open a fresh issue and provide a sample application that shows it.
Comment From: shihuncl
I met the same problem. Did you solve it? How did you solve it?
Comment From: kiranshiva
specify @JsonProperty
in entity class constructor like this.
```java @JsonCreator public Location(@JsonProperty("sl_no") Long sl_no, @JsonProperty("location")String location, @JsonProperty("location_type") String location_type, @JsonProperty("store_sl_no")Long store_sl_no) { this.sl_no = sl_no; this.location = location; this.location_type = location_type; this.store_sl_no = store_sl_no; }
Comment From: fernandongana
specify
@JsonProperty
in entity class constructor like this.
java @JsonCreator public Location(@JsonProperty("sl_no") Long sl_no, @JsonProperty("location")String location, @JsonProperty("location_type") String location_type, @JsonProperty("store_sl_no")Long store_sl_no) { this.sl_no = sl_no; this.location = location; this.location_type = location_type; this.store_sl_no = store_sl_no; }
Thanks Solved my Problem
Comment From: ElbekEshkuvvatov
hello