Browse Source

RequestParamMethodArgumentResolver defensively handles MethodParameter nesting level and java.util.Optional access

Issue: SPR-13850
pull/966/head
Juergen Hoeller 10 years ago
parent
commit
a58eee6ad1
  1. 22
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java
  2. 90
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolverTests.java

22
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part; import javax.servlet.http.Part;
@ -123,24 +122,23 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
} }
@Override @Override
@UsesJava8
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
assertIsMultipartRequest(servletRequest); assertIsMultipartRequest(servletRequest);
MultipartHttpServletRequest multipartRequest = MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class); WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
String partName = getPartName(parameter);
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
boolean optional = paramType.getName().equals("java.util.Optional"); boolean optional = paramType.getName().equals("java.util.Optional");
if (optional) { if (optional) {
parameter = new MethodParameter(parameter);
parameter.increaseNestingLevel(); parameter.increaseNestingLevel();
paramType = parameter.getNestedParameterType(); paramType = parameter.getNestedParameterType();
} }
String partName = getPartName(parameter);
Object arg; Object arg;
if (MultipartFile.class == paramType) { if (MultipartFile.class == paramType) {
@ -194,7 +192,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
throw new MissingServletRequestPartException(partName); throw new MissingServletRequestPartException(partName);
} }
if (optional) { if (optional) {
arg = Optional.ofNullable(arg); arg = OptionalResolver.resolveValue(arg);
} }
return arg; return arg;
@ -264,4 +262,16 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
} }
} }
/**
* Inner class to avoid hard-coded dependency on Java 8 Optional type...
*/
@UsesJava8
private static class OptionalResolver {
public static Object resolveValue(Object value) {
return Optional.ofNullable(value);
}
}
} }

90
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolverTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,7 +26,6 @@ import javax.servlet.http.Part;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import org.hamcrest.Matchers;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -157,14 +156,12 @@ public class RequestPartMethodArgumentResolverTests {
@Test @Test
public void resolveMultipartFile() throws Exception { public void resolveMultipartFile() throws Exception {
Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null); Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
assertNotNull(actual);
assertSame(multipartFile1, actual); assertSame(multipartFile1, actual);
} }
@Test @Test
public void resolveMultipartFileList() throws Exception { public void resolveMultipartFileList() throws Exception {
Object actual = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null); Object actual = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null);
assertNotNull(actual);
assertTrue(actual instanceof List); assertTrue(actual instanceof List);
assertEquals(Arrays.asList(multipartFile1, multipartFile2), actual); assertEquals(Arrays.asList(multipartFile1, multipartFile2), actual);
} }
@ -175,6 +172,7 @@ public class RequestPartMethodArgumentResolverTests {
assertNotNull(actual); assertNotNull(actual);
assertTrue(actual instanceof MultipartFile[]); assertTrue(actual instanceof MultipartFile[]);
MultipartFile[] parts = (MultipartFile[]) actual; MultipartFile[] parts = (MultipartFile[]) actual;
assertEquals(2, parts.length);
assertEquals(parts[0], multipartFile1); assertEquals(parts[0], multipartFile1);
assertEquals(parts[1], multipartFile2); assertEquals(parts[1], multipartFile2);
} }
@ -202,7 +200,6 @@ public class RequestPartMethodArgumentResolverTests {
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(paramPart, null, webRequest, null); Object result = resolver.resolveArgument(paramPart, null, webRequest, null);
assertTrue(result instanceof Part); assertTrue(result instanceof Part);
assertEquals("Invalid result", expected, result); assertEquals("Invalid result", expected, result);
} }
@ -219,7 +216,6 @@ public class RequestPartMethodArgumentResolverTests {
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(paramPartList, null, webRequest, null); Object result = resolver.resolveArgument(paramPartList, null, webRequest, null);
assertTrue(result instanceof List); assertTrue(result instanceof List);
assertEquals(Arrays.asList(part1, part2), result); assertEquals(Arrays.asList(part1, part2), result);
} }
@ -236,10 +232,9 @@ public class RequestPartMethodArgumentResolverTests {
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null); Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null);
assertTrue(result instanceof Part[]); assertTrue(result instanceof Part[]);
Part[] parts = (Part[]) result; Part[] parts = (Part[]) result;
assertThat(parts, Matchers.arrayWithSize(2)); assertEquals(2, parts.length);
assertEquals(parts[0], part1); assertEquals(parts[0], part1);
assertEquals(parts[1], part2); assertEquals(parts[1], part2);
} }
@ -302,8 +297,8 @@ public class RequestPartMethodArgumentResolverTests {
@Test // SPR-9079 @Test // SPR-9079
public void isMultipartRequestPut() throws Exception { public void isMultipartRequestPut() throws Exception {
this.multipartRequest.setMethod("PUT"); this.multipartRequest.setMethod("PUT");
Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null); Object actualValue = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
assertSame(multipartFile1, actual); assertSame(multipartFile1, actualValue);
} }
@Test @Test
@ -313,10 +308,13 @@ public class RequestPartMethodArgumentResolverTests {
request.addFile(expected); request.addFile(expected);
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
assertTrue(actualValue instanceof Optional);
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
assertTrue(result instanceof Optional); actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
assertEquals("Invalid result", expected, ((Optional) result).get()); assertTrue(actualValue instanceof Optional);
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
} }
@Test @Test
@ -324,10 +322,11 @@ public class RequestPartMethodArgumentResolverTests {
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
assertEquals("Invalid argument value", Optional.empty(), actualValue);
assertTrue(result instanceof Optional); actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
assertFalse("Invalid result", ((Optional) result).isPresent()); assertEquals("Invalid argument value", Optional.empty(), actualValue);
} }
@Test @Test
@ -339,10 +338,13 @@ public class RequestPartMethodArgumentResolverTests {
request.addPart(expected); request.addPart(expected);
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(optionalPart, null, webRequest, null); Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
assertTrue(actualValue instanceof Optional);
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
assertTrue(result instanceof Optional); actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
assertEquals("Invalid result", expected, ((Optional) result).get()); assertTrue(actualValue instanceof Optional);
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
} }
@Test @Test
@ -352,22 +354,26 @@ public class RequestPartMethodArgumentResolverTests {
request.setContentType("multipart/form-data"); request.setContentType("multipart/form-data");
webRequest = new ServletWebRequest(request); webRequest = new ServletWebRequest(request);
Object result = resolver.resolveArgument(optionalPart, null, webRequest, null); Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
assertEquals("Invalid argument value", Optional.empty(), actualValue);
assertTrue(result instanceof Optional); actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
assertFalse("Invalid result", ((Optional) result).isPresent()); assertEquals("Invalid argument value", Optional.empty(), actualValue);
} }
@Test @Test
public void resolveOptionalRequestPart() throws Exception { public void resolveOptionalRequestPart() throws Exception {
SimpleBean simpleBean = new SimpleBean("foo"); SimpleBean simpleBean = new SimpleBean("foo");
given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true); given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true);
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(simpleBean); given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(simpleBean);
ModelAndViewContainer mavContainer = new ModelAndViewContainer(); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory()); Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue);
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue); assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue);
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
} }
@ -378,8 +384,12 @@ public class RequestPartMethodArgumentResolverTests {
given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(null); given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(null);
ModelAndViewContainer mavContainer = new ModelAndViewContainer(); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory()); Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
assertEquals("Invalid argument value", Optional.empty(), actualValue);
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
assertEquals("Invalid argument value", Optional.empty(), actualValue); assertEquals("Invalid argument value", Optional.empty(), actualValue);
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
} }
@ -390,8 +400,8 @@ public class RequestPartMethodArgumentResolverTests {
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(argValue); given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(argValue);
ModelAndViewContainer mavContainer = new ModelAndViewContainer(); ModelAndViewContainer mavContainer = new ModelAndViewContainer();
Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
assertEquals("Invalid argument value", argValue, actualValue); assertEquals("Invalid argument value", argValue, actualValue);
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
} }
@ -425,22 +435,24 @@ public class RequestPartMethodArgumentResolverTests {
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void handle(@RequestPart SimpleBean requestPart, public void handle(
@RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart, @RequestPart SimpleBean requestPart,
@Valid @RequestPart("requestPart") SimpleBean validRequestPart, @RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart,
@RequestPart("requestPart") MultipartFile multipartFile, @Valid @RequestPart("requestPart") SimpleBean validRequestPart,
@RequestPart("requestPart") List<MultipartFile> multipartFileList, @RequestPart("requestPart") MultipartFile multipartFile,
@RequestPart("requestPart") MultipartFile[] multipartFileArray, @RequestPart("requestPart") List<MultipartFile> multipartFileList,
int i, @RequestPart("requestPart") MultipartFile[] multipartFileArray,
MultipartFile multipartFileNotAnnot, int i,
Part part, MultipartFile multipartFileNotAnnot,
@RequestPart("part") List<Part> partList, Part part,
@RequestPart("part") Part[] partArray, @RequestPart("requestPart") List<Part> partList,
@RequestParam MultipartFile requestParamAnnot, @RequestPart("requestPart") Part[] partArray,
Optional<MultipartFile> optionalMultipartFile, @RequestParam MultipartFile requestParamAnnot,
Optional<Part> optionalPart, Optional<MultipartFile> optionalMultipartFile,
@RequestPart("requestPart") Optional<SimpleBean> optionalRequestPart) { Optional<Part> optionalPart,
@RequestPart("requestPart") Optional<SimpleBean> optionalRequestPart) {
} }
} }

Loading…
Cancel
Save