spring-web:5.5.8
https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2
According to the RFC specification, a header does not allow multiple values, but multiple value lists separated by commas are allowed
When there isContent-type: application/x-www-form-urlencoded, application/x-www-form-urlencoded
Data cannot be read properly, but ifContent-type: ["application/x-www-form-urlencoded","application/x-www-form-urlencoded"]
, as shown in the following figure
curl -X POST -d "a=1&b=2" 127.0.0.1:8088/test -H "Content-type: application/x-www-form-urlencoded" -H "Content-type: application/x-www-form-urlencoded"
In the following case, the test object will be a null value
`curl -X POST -d "a=1&b=2" 127.0.0.1:8088/test -H "Content-type: application/x-www-form-urlencoded,application/x-www-form-urlencoded"
test case:
@PostMapping("/test")
@ResponseBody
void test(Test test, @RequestHeader("Content-Type") String contentType){
System.out.println(contentType);
System.out.println(test.getA()!=null);
}
public static class Test{
String a;
String b;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
}
`
Comment From: bclozel
The @RequestHeader
annotation merely resolves values from HTTP headers, but does not know about their actual meanings or conventions. It's up to you to parse them if needed. In this case, you can also use @RequestHeader("Content-Type") List<String> contentTypes
to convert them automatically to lists. In short, what you're seeing in @RequestHeader
does not represent internal content negotiation or how the web framework reasons about the incoming message.
Now according to the RFC, the Content-Type
header must only include a single media-type value so the use case you are showing here is invalid. Because the web framework cannot parse properly the incoming content-type, form binding does not operate and this explains the behavior you are seeing.
I'm closing this issue as invalid.
Comment From: funky-eyes
The
@RequestHeader
annotation merely resolves values from HTTP headers, but does not know about their actual meanings or conventions. It's up to you to parse them if needed. In this case, you can also use@RequestHeader("Content-Type") List<String> contentTypes
to convert them automatically to lists. In short, what you're seeing in@RequestHeader
does not represent internal content negotiation or how the web framework reasons about the incoming message.Now according to the RFC, the
Content-Type
header must only include a single media-type value so the use case you are showing here is invalid. Because the web framework cannot parse properly the incoming content-type, form binding does not operate and this explains the behavior you are seeing.I'm closing this issue as invalid.
So why can adding multiple content-type
in the form of an array read form data?
Comment From: bclozel
I don't know. Both cases are invalid anyway. Is there a improvement to be made or a bug here?
Comment From: funky-eyes
I don't know. Both cases are invalid anyway. Is there a improvement to be made or a bug here?
curl -X POST -d "a=1&b=2" 127.0.0.1:8088/test -H "Content-type: application/x-www-form-urlencoded" -H "Content-type: application/x-www-form-urlencoded"
This example is obviously valid, the code I have provided, and the test results have also been provided
Comment From: bclozel
The very first line of your report states that this type of request is invalid according to the RFC
https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2 According to the RFC specification, a header does not allow multiple values, but multiple value lists separated by commas are allowed
Comment From: bclozel
If you change your repro case to use the following:
@PostMapping("/test")
@ResponseBody
void test(HttpServletRequest request, Test test) {
System.out.println(request.getContentType());
System.out.println(test.getA());
}
You will see that it's actually the Servlet container that is enforcing the behavior you are describing, not Spring. I haven't seen anything special about this in the Servlet spec so I assume this is up to Servlet container implementations.
Comment From: funky-eyes
If you change your repro case to use the following:
java @PostMapping("/test") @ResponseBody void test(HttpServletRequest request, Test test) { System.out.println(request.getContentType()); System.out.println(test.getA()); }
You will see that it's actually the Servlet container that is enforcing the behavior you are describing, not Spring. I haven't seen anything special about this in the Servlet spec so I assume this is up to Servlet container implementations.
thx