Empty body checks in ConsumesRequestCondition

Normally consumes matches the "Content-Type" header but what should be done if
there is no content? This commit adds checks for method parameters with
@RequestBody(required=false) and if "false" then also match requests with no content.

Closes gh-22010
This commit is contained in:
Rossen Stoyanchev
2019-05-07 22:00:34 -04:00
parent cdf51c3d51
commit 45147c23c1
8 changed files with 239 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.util.Collections;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression;
@@ -107,6 +108,27 @@ public class ConsumesRequestConditionTests {
assertNull(condition.getMatchingCondition(request));
}
@Test // gh-22010
public void consumesNoContent() {
ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain");
condition.setBodyRequired(false);
MockHttpServletRequest request = new MockHttpServletRequest();
assertNotNull(condition.getMatchingCondition(request));
request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.CONTENT_LENGTH, "0");
assertNotNull(condition.getMatchingCondition(request));
request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.CONTENT_LENGTH, "21");
assertNull(condition.getMatchingCondition(request));
request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.TRANSFER_ENCODING, "chunked");
assertNull(condition.getMatchingCondition(request));
}
@Test
public void compareToSingle() {
MockHttpServletRequest request = new MockHttpServletRequest();

View File

@@ -22,7 +22,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -34,6 +33,7 @@ import org.junit.Test;
import org.springframework.core.annotation.AliasFor;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -41,11 +41,14 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerTypePredicate;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import static org.junit.Assert.*;
@@ -165,10 +168,23 @@ public class RequestMappingHandlerMappingTests {
@Test // SPR-14988
public void getMappingOverridesConsumesFromTypeLevelAnnotation() throws Exception {
RequestMappingInfo requestMappingInfo = assertComposedAnnotationMapping(RequestMethod.GET);
RequestMappingInfo requestMappingInfo = assertComposedAnnotationMapping(RequestMethod.POST);
assertArrayEquals(new MediaType[]{MediaType.ALL}, new ArrayList<>(
requestMappingInfo.getConsumesCondition().getConsumableMediaTypes()).toArray());
ConsumesRequestCondition condition = requestMappingInfo.getConsumesCondition();
assertEquals(Collections.singleton(MediaType.APPLICATION_XML), condition.getConsumableMediaTypes());
}
@Test // gh-22010
public void consumesWithOptionalRequestBody() {
this.wac.registerSingleton("testController", ComposedAnnotationController.class);
this.wac.refresh();
this.handlerMapping.afterPropertiesSet();
RequestMappingInfo info = this.handlerMapping.getHandlerMethods().keySet().stream()
.filter(i -> i.getPatternsCondition().getPatterns().equals(Collections.singleton("/post")))
.findFirst()
.orElseThrow(() -> new AssertionError("No /post"));
assertFalse(info.getConsumesCondition().isBodyRequired());
}
@Test
@@ -207,7 +223,7 @@ public class RequestMappingHandlerMappingTests {
RequestMethod requestMethod) throws Exception {
Class<?> clazz = ComposedAnnotationController.class;
Method method = clazz.getMethod(methodName);
Method method = ClassUtils.getMethod(clazz, methodName, null);
RequestMappingInfo info = this.handlerMapping.getMappingForMethod(method, clazz);
assertNotNull(info);
@@ -236,12 +252,12 @@ public class RequestMappingHandlerMappingTests {
public void postJson() {
}
@GetMapping(path = "/get", consumes = MediaType.ALL_VALUE)
@GetMapping("/get")
public void get() {
}
@PostMapping("/post")
public void post() {
@PostMapping(path = "/post", consumes = MediaType.APPLICATION_XML_VALUE)
public void post(@RequestBody(required = false) Foo foo) {
}
@PutMapping("/put")
@@ -281,4 +297,8 @@ public class RequestMappingHandlerMappingTests {
}
}
private static class Foo {
}
}