From 02d727fd7c9f6fe7b5d610c4cb112651c2a3e4d7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 12 Jan 2017 21:17:54 +0100 Subject: [PATCH] MockHttpServletRequestBuilder supports multiple locales Includes revised content type handling. Issue: SPR-15116 --- .../mock/web/MockHttpServletRequest.java | 41 +- .../MockHttpServletRequestBuilder.java | 437 +++++++++--------- .../MockHttpServletRequestBuilderTests.java | 69 +-- .../mock/web/test/MockHttpServletRequest.java | 41 +- 4 files changed, 277 insertions(+), 311 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index f900e865491..00204a89640 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -422,6 +422,7 @@ public class MockHttpServletRequest implements HttpServletRequest { return (this.content != null ? this.content.length : -1); } + @Override public long getContentLengthLong() { return getContentLength(); } @@ -475,28 +476,25 @@ public class MockHttpServletRequest implements HttpServletRequest { *

If there are already one or more values registered for the given * parameter name, they will be replaced. */ - public void setParameter(String name, String[] values) { + public void setParameter(String name, String... values) { Assert.notNull(name, "Parameter name must not be null"); this.parameters.put(name, values); } /** - * Sets all provided parameters replacing any existing + * Set all provided parameters replacing any existing * values for the provided parameter names. To add without replacing * existing values, use {@link #addParameters(java.util.Map)}. */ - @SuppressWarnings("rawtypes") - public void setParameters(Map params) { + public void setParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); - for (Object key : params.keySet()) { - Assert.isInstanceOf(String.class, key, - "Parameter map key must be of type [" + String.class.getName() + "]"); + for (String key : params.keySet()) { Object value = params.get(key); if (value instanceof String) { - this.setParameter((String) key, (String) value); + setParameter(key, (String) value); } else if (value instanceof String[]) { - this.setParameter((String) key, (String[]) value); + setParameter(key, (String[]) value); } else { throw new IllegalArgumentException( @@ -519,7 +517,7 @@ public class MockHttpServletRequest implements HttpServletRequest { *

If there are already one or more values registered for the given * parameter name, the given values will be added to the end of the list. */ - public void addParameter(String name, String[] values) { + public void addParameter(String name, String... values) { Assert.notNull(name, "Parameter name must not be null"); String[] oldArr = this.parameters.get(name); if (oldArr != null) { @@ -538,18 +536,15 @@ public class MockHttpServletRequest implements HttpServletRequest { * existing values. To replace existing values, use * {@link #setParameters(java.util.Map)}. */ - @SuppressWarnings("rawtypes") - public void addParameters(Map params) { + public void addParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); - for (Object key : params.keySet()) { - Assert.isInstanceOf(String.class, key, - "Parameter map key must be of type [" + String.class.getName() + "]"); + for (String key : params.keySet()) { Object value = params.get(key); if (value instanceof String) { - this.addParameter((String) key, (String) value); + this.addParameter(key, (String) value); } else if (value instanceof String[]) { - this.addParameter((String) key, (String[]) value); + this.addParameter(key, (String[]) value); } else { throw new IllegalArgumentException("Parameter map value must be single value " + @@ -929,14 +924,14 @@ public class MockHttpServletRequest implements HttpServletRequest { * @see #getDateHeader */ public void addHeader(String name, Object value) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) { - setContentType((String) value); - return; + if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name) && !this.headers.containsKey(CONTENT_TYPE_HEADER)) { + setContentType(value.toString()); + } + else { + doAddHeaderValue(name, value, false); } - doAddHeaderValue(name, value, false); } - @SuppressWarnings("rawtypes") private void doAddHeaderValue(String name, Object value, boolean replace) { HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); Assert.notNull(value, "Header value must not be null"); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index 0c1c0007850..944507ba181 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -50,7 +50,6 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.servlet.DispatcherServlet; @@ -83,37 +82,37 @@ public class MockHttpServletRequestBuilder private final URI url; - private final MultiValueMap headers = new LinkedMultiValueMap<>(); + private String contextPath = ""; - private String contentType; + private String servletPath = ""; - private byte[] content; + private String pathInfo = ""; - private final MultiValueMap parameters = new LinkedMultiValueMap<>(); + private Boolean secure; - private final List cookies = new ArrayList<>(); + private Principal principal; - private Locale locale; + private MockHttpSession session; private String characterEncoding; - private Boolean secure; + private byte[] content; - private Principal principal; + private String contentType; - private final Map attributes = new LinkedHashMap<>(); + private final MultiValueMap headers = new LinkedMultiValueMap<>(); - private MockHttpSession session; + private final MultiValueMap parameters = new LinkedMultiValueMap<>(); - private final Map sessionAttributes = new LinkedHashMap<>(); + private final List cookies = new ArrayList<>(); - private final Map flashAttributes = new LinkedHashMap<>(); + private final List locales = new ArrayList<>(); - private String contextPath = ""; + private final Map requestAttributes = new LinkedHashMap<>(); - private String servletPath = ""; + private final Map sessionAttributes = new LinkedHashMap<>(); - private String pathInfo = ValueConstants.DEFAULT_NONE; + private final Map flashAttributes = new LinkedHashMap<>(); private final List postProcessors = new ArrayList<>(); @@ -158,59 +157,95 @@ public class MockHttpServletRequestBuilder /** - * Add a request parameter to the {@link MockHttpServletRequest}. - *

If called more than once, new values get added to existing ones. - * @param name the parameter name - * @param values one or more values + * Specify the portion of the requestURI that represents the context path. + * The context path, if specified, must match to the start of the request URI. + *

In most cases, tests can be written by omitting the context path from + * the requestURI. This is because most applications don't actually depend + * on the name under which they're deployed. If specified here, the context + * path must start with a "/" and must not end with a "/". + * @see javax.servlet.http.HttpServletRequest#getContextPath() */ - public MockHttpServletRequestBuilder param(String name, String... values) { - addToMultiValueMap(this.parameters, name, values); + public MockHttpServletRequestBuilder contextPath(String contextPath) { + if (StringUtils.hasText(contextPath)) { + Assert.isTrue(contextPath.startsWith("/"), "Context path must start with a '/'"); + Assert.isTrue(!contextPath.endsWith("/"), "Context path must not end with a '/'"); + } + this.contextPath = (contextPath != null ? contextPath : ""); return this; } /** - * Add a map of request parameters to the {@link MockHttpServletRequest}, - * for example when testing a form submission. - *

If called more than once, new values get added to existing ones. - * @param params the parameters to add - * @since 4.2.4 + * Specify the portion of the requestURI that represents the path to which + * the Servlet is mapped. This is typically a portion of the requestURI + * after the context path. + *

In most cases, tests can be written by omitting the servlet path from + * the requestURI. This is because most applications don't actually depend + * on the prefix to which a servlet is mapped. For example if a Servlet is + * mapped to {@code "/main/*"}, tests can be written with the requestURI + * {@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}. + * If specified here, the servletPath must start with a "/" and must not + * end with a "/". + * @see javax.servlet.http.HttpServletRequest#getServletPath() */ - public MockHttpServletRequestBuilder params(MultiValueMap params) { - for (String name : params.keySet()) { - for (String value : params.get(name)) { - this.parameters.add(name, value); - } + public MockHttpServletRequestBuilder servletPath(String servletPath) { + if (StringUtils.hasText(servletPath)) { + Assert.isTrue(servletPath.startsWith("/"), "Servlet path must start with a '/'"); + Assert.isTrue(!servletPath.endsWith("/"), "Servlet path must not end with a '/'"); } + this.servletPath = (servletPath != null ? servletPath : ""); return this; } /** - * Add a header to the request. Values are always added. - * @param name the header name - * @param values one or more header values + * Specify the portion of the requestURI that represents the pathInfo. + *

If left unspecified (recommended), the pathInfo will be automatically derived + * by removing the contextPath and the servletPath from the requestURI and using any + * remaining part. If specified here, the pathInfo must start with a "/". + *

If specified, the pathInfo will be used as-is. + * @see javax.servlet.http.HttpServletRequest#getPathInfo() */ - public MockHttpServletRequestBuilder header(String name, Object... values) { - if ("Content-Type".equalsIgnoreCase(name)) { - List mediaTypes = MediaType.parseMediaTypes(StringUtils.arrayToCommaDelimitedString(values)); - this.contentType = MediaType.toString(mediaTypes); + public MockHttpServletRequestBuilder pathInfo(String pathInfo) { + if (StringUtils.hasText(pathInfo)) { + Assert.isTrue(pathInfo.startsWith("/"), "Path info must start with a '/'"); } - addToMultiValueMap(this.headers, name, values); + this.pathInfo = pathInfo; return this; } /** - * Add all headers to the request. Values are always added. - * @param httpHeaders the headers and values to add + * Set the secure property of the {@link ServletRequest} indicating use of a + * secure channel, such as HTTPS. + * @param secure whether the request is using a secure channel */ - public MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) { - MediaType mediaType = httpHeaders.getContentType(); - if (mediaType != null) { - this.contentType = mediaType.toString(); - } - for (String name : httpHeaders.keySet()) { - Object[] values = ObjectUtils.toObjectArray(httpHeaders.get(name).toArray()); - addToMultiValueMap(this.headers, name, values); - } + public MockHttpServletRequestBuilder secure(boolean secure){ + this.secure = secure; + return this; + } + + /** + * Set the character encoding of the request. + * @param encoding the character encoding + */ + public MockHttpServletRequestBuilder characterEncoding(String encoding) { + this.characterEncoding = encoding; + return this; + } + + /** + * Set the request body. + * @param content the body content + */ + public MockHttpServletRequestBuilder content(byte[] content) { + this.content = content; + return this; + } + + /** + * Set the request body as a UTF-8 String. + * @param content the body content + */ + public MockHttpServletRequestBuilder content(String content) { + this.content = content.getBytes(StandardCharsets.UTF_8); return this; } @@ -221,7 +256,6 @@ public class MockHttpServletRequestBuilder public MockHttpServletRequestBuilder contentType(MediaType contentType) { Assert.notNull(contentType, "'contentType' must not be null"); this.contentType = contentType.toString(); - this.headers.set("Content-Type", this.contentType); return this; } @@ -232,7 +266,6 @@ public class MockHttpServletRequestBuilder */ public MockHttpServletRequestBuilder contentType(String contentType) { this.contentType = MediaType.parseMediaType(contentType).toString(); - this.headers.set("Content-Type", this.contentType); return this; } @@ -261,20 +294,51 @@ public class MockHttpServletRequestBuilder } /** - * Set the request body. - * @param content the body content + * Add a header to the request. Values are always added. + * @param name the header name + * @param values one or more header values */ - public MockHttpServletRequestBuilder content(byte[] content) { - this.content = content; + public MockHttpServletRequestBuilder header(String name, Object... values) { + addToMultiValueMap(this.headers, name, values); return this; } /** - * Set the request body as a UTF-8 String. - * @param content the body content + * Add all headers to the request. Values are always added. + * @param httpHeaders the headers and values to add */ - public MockHttpServletRequestBuilder content(String content) { - this.content = content.getBytes(StandardCharsets.UTF_8); + public MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) { + for (String name : httpHeaders.keySet()) { + Object[] values = ObjectUtils.toObjectArray(httpHeaders.get(name).toArray()); + addToMultiValueMap(this.headers, name, values); + } + return this; + } + + /** + * Add a request parameter to the {@link MockHttpServletRequest}. + *

If called more than once, new values get added to existing ones. + * @param name the parameter name + * @param values one or more values + */ + public MockHttpServletRequestBuilder param(String name, String... values) { + addToMultiValueMap(this.parameters, name, values); + return this; + } + + /** + * Add a map of request parameters to the {@link MockHttpServletRequest}, + * for example when testing a form submission. + *

If called more than once, new values get added to existing ones. + * @param params the parameters to add + * @since 4.2.4 + */ + public MockHttpServletRequestBuilder params(MultiValueMap params) { + for (String name : params.keySet()) { + for (String value : params.get(name)) { + this.parameters.add(name, value); + } + } return this; } @@ -289,20 +353,27 @@ public class MockHttpServletRequestBuilder } /** - * Set the locale of the request. - * @param locale the locale + * Add the specified locales as preferred request locales. + * @param locales the locales to add + * @since 4.3.6 + * @see #locale(Locale) */ - public MockHttpServletRequestBuilder locale(Locale locale) { - this.locale = locale; + public MockHttpServletRequestBuilder locale(Locale... locales) { + Assert.notEmpty(locales, "'locales' must not be empty"); + this.locales.addAll(Arrays.asList(locales)); return this; } /** - * Set the character encoding of the request. - * @param encoding the character encoding + * Set the locale of the request, overriding any previous locales. + * @param locale the locale, or {@code null} to reset it + * @see #locale(Locale...) */ - public MockHttpServletRequestBuilder characterEncoding(String encoding) { - this.characterEncoding = encoding; + public MockHttpServletRequestBuilder locale(Locale locale) { + this.locales.clear(); + if (locale != null) { + this.locales.add(locale); + } return this; } @@ -312,7 +383,7 @@ public class MockHttpServletRequestBuilder * @param value the attribute value */ public MockHttpServletRequestBuilder requestAttr(String name, Object value) { - addToMap(this.attributes, name, value); + addToMap(this.requestAttributes, name, value); return this; } @@ -382,73 +453,6 @@ public class MockHttpServletRequestBuilder return this; } - /** - * Specify the portion of the requestURI that represents the context path. - * The context path, if specified, must match to the start of the request URI. - *

In most cases, tests can be written by omitting the context path from - * the requestURI. This is because most applications don't actually depend - * on the name under which they're deployed. If specified here, the context - * path must start with a "/" and must not end with a "/". - * @see HttpServletRequest.getContextPath() - */ - public MockHttpServletRequestBuilder contextPath(String contextPath) { - if (StringUtils.hasText(contextPath)) { - Assert.isTrue(contextPath.startsWith("/"), "Context path must start with a '/'"); - Assert.isTrue(!contextPath.endsWith("/"), "Context path must not end with a '/'"); - } - this.contextPath = (contextPath != null) ? contextPath : ""; - return this; - } - - /** - * Specify the portion of the requestURI that represents the path to which - * the Servlet is mapped. This is typically a portion of the requestURI - * after the context path. - *

In most cases, tests can be written by omitting the servlet path from - * the requestURI. This is because most applications don't actually depend - * on the prefix to which a servlet is mapped. For example if a Servlet is - * mapped to {@code "/main/*"}, tests can be written with the requestURI - * {@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}. - * If specified here, the servletPath must start with a "/" and must not - * end with a "/". - * @see HttpServletRequest.getServletPath() - */ - public MockHttpServletRequestBuilder servletPath(String servletPath) { - if (StringUtils.hasText(servletPath)) { - Assert.isTrue(servletPath.startsWith("/"), "Servlet path must start with a '/'"); - Assert.isTrue(!servletPath.endsWith("/"), "Servlet path must not end with a '/'"); - } - this.servletPath = (servletPath != null) ? servletPath : ""; - return this; - } - - /** - * Specify the portion of the requestURI that represents the pathInfo. - *

If left unspecified (recommended), the pathInfo will be automatically - * derived by removing the contextPath and the servletPath from the - * requestURI and using any remaining part. If specified here, the pathInfo - * must start with a "/". - *

If specified, the pathInfo will be used as is. - * @see HttpServletRequest.getServletPath() - */ - public MockHttpServletRequestBuilder pathInfo(String pathInfo) { - if (StringUtils.hasText(pathInfo)) { - Assert.isTrue(pathInfo.startsWith("/"), "pathInfo must start with a '/'"); - } - this.pathInfo = pathInfo; - return this; - } - - /** - * Set the secure property of the {@link ServletRequest} indicating use of a - * secure channel, such as HTTPS. - * @param secure whether the request is using a secure channel - */ - public MockHttpServletRequestBuilder secure(boolean secure){ - this.secure = secure; - return this; - } - /** * An extension point for further initialization of {@link MockHttpServletRequest} * in ways not built directly into the {@code MockHttpServletRequestBuilder}. @@ -489,19 +493,41 @@ public class MockHttpServletRequestBuilder } MockHttpServletRequestBuilder parentBuilder = (MockHttpServletRequestBuilder) parent; - for (String headerName : parentBuilder.headers.keySet()) { - if (!this.headers.containsKey(headerName)) { - this.headers.put(headerName, parentBuilder.headers.get(headerName)); - } + if (!StringUtils.hasText(this.contextPath)) { + this.contextPath = parentBuilder.contextPath; + } + if (!StringUtils.hasText(this.servletPath)) { + this.servletPath = parentBuilder.servletPath; + } + if ("".equals(this.pathInfo)) { + this.pathInfo = parentBuilder.pathInfo; } - if (this.contentType == null) { - this.contentType = parentBuilder.contentType; + if (this.secure == null) { + this.secure = parentBuilder.secure; + } + if (this.principal == null) { + this.principal = parentBuilder.principal; + } + if (this.session == null) { + this.session = parentBuilder.session; + } + + if (this.characterEncoding == null) { + this.characterEncoding = parentBuilder.characterEncoding; } if (this.content == null) { this.content = parentBuilder.content; } + if (this.contentType == null) { + this.contentType = parentBuilder.contentType; + } + for (String headerName : parentBuilder.headers.keySet()) { + if (!this.headers.containsKey(headerName)) { + this.headers.put(headerName, parentBuilder.headers.get(headerName)); + } + } for (String paramName : parentBuilder.parameters.keySet()) { if (!this.parameters.containsKey(paramName)) { this.parameters.put(paramName, parentBuilder.parameters.get(paramName)); @@ -512,53 +538,26 @@ public class MockHttpServletRequestBuilder this.cookies.add(cookie); } } - - if (this.locale == null) { - this.locale = parentBuilder.locale; - } - if (this.characterEncoding == null) { - this.characterEncoding = parentBuilder.characterEncoding; - } - - if (this.secure == null) { - this.secure = parentBuilder.secure; - } - if (this.principal == null) { - this.principal = parentBuilder.principal; - } - - for (String attributeName : parentBuilder.attributes.keySet()) { - if (!this.attributes.containsKey(attributeName)) { - this.attributes.put(attributeName, parentBuilder.attributes.get(attributeName)); + for (Locale locale : parentBuilder.locales) { + if (!this.locales.contains(locale)) { + this.locales.add(locale); } } - if (this.session == null) { - this.session = parentBuilder.session; - } - - for (String sessionAttributeName : parentBuilder.sessionAttributes.keySet()) { - if (!this.sessionAttributes.containsKey(sessionAttributeName)) { - this.sessionAttributes.put(sessionAttributeName, parentBuilder.sessionAttributes.get(sessionAttributeName)); + for (String attributeName : parentBuilder.requestAttributes.keySet()) { + if (!this.requestAttributes.containsKey(attributeName)) { + this.requestAttributes.put(attributeName, parentBuilder.requestAttributes.get(attributeName)); } } - - for (String flashAttributeName : parentBuilder.flashAttributes.keySet()) { - if (!this.flashAttributes.containsKey(flashAttributeName)) { - this.flashAttributes.put(flashAttributeName, parentBuilder.flashAttributes.get(flashAttributeName)); + for (String attributeName : parentBuilder.sessionAttributes.keySet()) { + if (!this.sessionAttributes.containsKey(attributeName)) { + this.sessionAttributes.put(attributeName, parentBuilder.sessionAttributes.get(attributeName)); } } - - if (!StringUtils.hasText(this.contextPath)) { - this.contextPath = parentBuilder.contextPath; - } - - if (!StringUtils.hasText(this.servletPath)) { - this.servletPath = parentBuilder.servletPath; - } - - if (ValueConstants.DEFAULT_NONE.equals(this.pathInfo)) { - this.pathInfo = parentBuilder.pathInfo; + for (String attributeName : parentBuilder.flashAttributes.keySet()) { + if (!this.flashAttributes.containsKey(attributeName)) { + this.flashAttributes.put(attributeName, parentBuilder.flashAttributes.get(attributeName)); + } } this.postProcessors.addAll(0, parentBuilder.postProcessors); @@ -582,9 +581,11 @@ public class MockHttpServletRequestBuilder public final MockHttpServletRequest buildRequest(ServletContext servletContext) { MockHttpServletRequest request = createServletRequest(servletContext); + request.setAsyncSupported(true); + request.setMethod(this.method); + String requestUri = this.url.getRawPath(); request.setRequestURI(requestUri); - updatePathRequestProperties(request, requestUri); if (this.url.getScheme() != null) { request.setScheme(this.url.getScheme()); @@ -596,7 +597,21 @@ public class MockHttpServletRequestBuilder request.setServerPort(this.url.getPort()); } - request.setMethod(this.method); + updatePathRequestProperties(request, requestUri); + + if (this.secure != null) { + request.setSecure(this.secure); + } + if (this.principal != null) { + request.setUserPrincipal(this.principal); + } + if (this.session != null) { + request.setSession(this.session); + } + + request.setCharacterEncoding(this.characterEncoding); + request.setContent(this.content); + request.setContentType(this.contentType); for (String name : this.headers.keySet()) { for (Object value : this.headers.get(name)) { @@ -615,10 +630,6 @@ public class MockHttpServletRequestBuilder } } - request.setContentType(this.contentType); - request.setContent(this.content); - request.setCharacterEncoding(this.characterEncoding); - if (this.content != null && this.contentType != null) { MediaType mediaType = MediaType.parseMediaType(this.contentType); if (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)) { @@ -629,24 +640,12 @@ public class MockHttpServletRequestBuilder if (!ObjectUtils.isEmpty(this.cookies)) { request.setCookies(this.cookies.toArray(new Cookie[this.cookies.size()])); } - - if (this.locale != null) { - request.addPreferredLocale(this.locale); - } - - if (this.secure != null) { - request.setSecure(this.secure); - } - - request.setUserPrincipal(this.principal); - - for (String name : this.attributes.keySet()) { - request.setAttribute(name, this.attributes.get(name)); + if (!ObjectUtils.isEmpty(this.locales)) { + request.setPreferredLocales(this.locales); } - // Set session before session and flash attributes - if (this.session != null) { - request.setSession(this.session); + for (String name : this.requestAttributes.keySet()) { + request.setAttribute(name, this.requestAttributes.get(name)); } for (String name : this.sessionAttributes.keySet()) { request.getSession().setAttribute(name, this.sessionAttributes.get(name)); @@ -654,12 +653,9 @@ public class MockHttpServletRequestBuilder FlashMap flashMap = new FlashMap(); flashMap.putAll(this.flashAttributes); - FlashMapManager flashMapManager = getFlashMapManager(request); flashMapManager.saveOutputFlashMap(flashMap, request, new MockHttpServletResponse()); - request.setAsyncSupported(true); - return request; } @@ -676,15 +672,20 @@ public class MockHttpServletRequestBuilder * Update the contextPath, servletPath, and pathInfo of the request. */ private void updatePathRequestProperties(MockHttpServletRequest request, String requestUri) { - Assert.isTrue(requestUri.startsWith(this.contextPath), - "requestURI [" + requestUri + "] does not start with contextPath [" + this.contextPath + "]"); + if (!requestUri.startsWith(this.contextPath)) { + throw new IllegalArgumentException( + "Request URI [" + requestUri + "] does not start with context path [" + this.contextPath + "]"); + } request.setContextPath(this.contextPath); request.setServletPath(this.servletPath); - if (ValueConstants.DEFAULT_NONE.equals(this.pathInfo)) { - Assert.isTrue(requestUri.startsWith(this.contextPath + this.servletPath), - "Invalid servletPath [" + this.servletPath + "] for requestURI [" + requestUri + "]"); + + if ("".equals(this.pathInfo)) { + if (!requestUri.startsWith(this.contextPath + this.servletPath)) { + throw new IllegalArgumentException( + "Invalid servlet path [" + this.servletPath + "] for request URI [" + requestUri + "]"); + } String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length()); - this.pathInfo = (StringUtils.hasText(extraPath)) ? extraPath : null; + this.pathInfo = (StringUtils.hasText(extraPath) ? extraPath : null); } request.setPathInfo(this.pathInfo); } @@ -704,13 +705,11 @@ public class MockHttpServletRequestBuilder } private MultiValueMap parseFormData(final MediaType mediaType) { - MultiValueMap map; HttpInputMessage message = new HttpInputMessage() { @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream(content); } - @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); @@ -718,13 +717,13 @@ public class MockHttpServletRequestBuilder return headers; } }; + try { - map = new FormHttpMessageConverter().read(null, message); + return new FormHttpMessageConverter().read(null, message); } catch (IOException ex) { throw new IllegalStateException("Failed to parse form data in request body", ex); } - return map; } private FlashMapManager getFlashMapManager(MockHttpServletRequest request) { diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java index a99fea8399b..1b4ae580153 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -46,10 +46,7 @@ import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.support.SessionFlashMapManager; import org.springframework.web.util.UriComponentsBuilder; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; /** * Unit tests for building a {@link MockHttpServletRequest} with @@ -70,6 +67,7 @@ public class MockHttpServletRequestBuilderTests { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/foo/bar"); } + @Test public void method() { MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); @@ -100,9 +98,7 @@ public class MockHttpServletRequestBuilderTests { assertEquals("/foo%20bar", request.getRequestURI()); } - // SPR-13435 - - @Test + @Test // SPR-13435 public void requestUriWithDoubleSlashes() throws URISyntaxException { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, new URI("/test//currentlyValid/0")); MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); @@ -113,7 +109,6 @@ public class MockHttpServletRequestBuilderTests { @Test public void contextPathEmpty() { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/foo"); - MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); assertEquals("", request.getContextPath()); @@ -125,7 +120,6 @@ public class MockHttpServletRequestBuilderTests { public void contextPathServletPathEmpty() { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/travel/hotels/42"); this.builder.contextPath("/travel"); - MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); assertEquals("/travel", request.getContextPath()); @@ -149,10 +143,8 @@ public class MockHttpServletRequestBuilderTests { @Test public void contextPathServletPathInfoEmpty() { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/travel/hotels/42"); - this.builder.contextPath("/travel"); this.builder.servletPath("/hotels/42"); - MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); assertEquals("/travel", request.getContextPath()); @@ -165,7 +157,6 @@ public class MockHttpServletRequestBuilderTests { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/"); this.builder.servletPath("/index.html"); this.builder.pathInfo(null); - MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); assertEquals("", request.getContextPath()); @@ -175,12 +166,11 @@ public class MockHttpServletRequestBuilderTests { @Test public void contextPathServletPathInvalid() { - - testContextPathServletPathInvalid("/Foo", "", "requestURI [/foo/bar] does not start with contextPath [/Foo]"); + testContextPathServletPathInvalid("/Foo", "", "Request URI [/foo/bar] does not start with context path [/Foo]"); testContextPathServletPathInvalid("foo", "", "Context path must start with a '/'"); testContextPathServletPathInvalid("/foo/", "", "Context path must not end with a '/'"); - testContextPathServletPathInvalid("/foo", "/Bar", "Invalid servletPath [/Bar] for requestURI [/foo/bar]"); + testContextPathServletPathInvalid("/foo", "/Bar", "Invalid servlet path [/Bar] for request URI [/foo/bar]"); testContextPathServletPathInvalid("/foo", "bar", "Servlet path must start with a '/'"); testContextPathServletPathInvalid("/foo", "/bar/", "Servlet path must not end with a '/'"); } @@ -246,9 +236,7 @@ public class MockHttpServletRequestBuilderTests { assertEquals("bar=baz", request.getParameter("foo")); } - // SPR-11043 - - @Test + @Test // SPR-11043 public void requestParameterFromQueryNull() { this.builder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/?foo"); @@ -259,9 +247,7 @@ public class MockHttpServletRequestBuilderTests { assertEquals("foo", request.getQueryString()); } - // SPR-13801 - - @Test + @Test // SPR-13801 public void requestParameterFromMultiValueMap() throws Exception { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("foo", "bar"); @@ -327,9 +313,7 @@ public class MockHttpServletRequestBuilderTests { assertEquals("text/html", contentTypes.get(0)); } - // SPR-11308 - - @Test + @Test // SPR-11308 public void contentTypeViaHeader() { this.builder.header("Content-Type", MediaType.TEXT_HTML_VALUE); MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); @@ -338,15 +322,12 @@ public class MockHttpServletRequestBuilderTests { assertEquals("text/html", contentType); } - // SPR-11308 - - @Test + @Test // SPR-11308 public void contentTypeViaMultipleHeaderValues() { this.builder.header("Content-Type", MediaType.TEXT_HTML_VALUE, MediaType.ALL_VALUE); MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); - String contentType = request.getContentType(); - assertEquals("text/html, */*", contentType); + assertEquals("text/html", request.getContentType()); } @Test @@ -490,36 +471,29 @@ public class MockHttpServletRequestBuilderTests { assertEquals(user, request.getUserPrincipal()); } - // SPR-12945 - - @Test + @Test // SPR-12945 public void mergeInvokesDefaultRequestPostProcessorFirst() { final String ATTR = "ATTR"; - final String EXEPCTED = "override"; + final String EXPECTED = "override"; MockHttpServletRequestBuilder defaultBuilder = new MockHttpServletRequestBuilder(HttpMethod.GET, "/foo/bar") - .with(requestAttr(ATTR).value("default")); - - builder - .with(requestAttr(ATTR).value(EXEPCTED)); + .with(requestAttr(ATTR).value("default")) + .with(requestAttr(ATTR).value(EXPECTED)); builder.merge(defaultBuilder); MockHttpServletRequest request = builder.buildRequest(servletContext); request = builder.postProcessRequest(request); - assertEquals(EXEPCTED, request.getAttribute(ATTR)); + assertEquals(EXPECTED, request.getAttribute(ATTR)); } - // SPR-13719 - - @Test + @Test // SPR-13719 public void arbitraryMethod() { String httpMethod = "REPort"; URI url = UriComponentsBuilder.fromPath("/foo/{bar}").buildAndExpand(42).toUri(); this.builder = new MockHttpServletRequestBuilder(httpMethod, url); - MockHttpServletRequest request = this.builder.buildRequest(this.servletContext); assertEquals(httpMethod, request.getMethod()); @@ -527,6 +501,11 @@ public class MockHttpServletRequestBuilderTests { } + private static RequestAttributePostProcessor requestAttr(String attrName) { + return new RequestAttributePostProcessor().attr(attrName); + } + + private final class User implements Principal { @Override @@ -535,9 +514,6 @@ public class MockHttpServletRequestBuilderTests { } } - private static RequestAttributePostProcessor requestAttr(String attrName) { - return new RequestAttributePostProcessor().attr(attrName); - } private static class RequestAttributePostProcessor implements RequestPostProcessor { @@ -560,4 +536,5 @@ public class MockHttpServletRequestBuilderTests { return request; } } + } diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java index 7c75496b2a9..56013a17449 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -422,6 +422,7 @@ public class MockHttpServletRequest implements HttpServletRequest { return (this.content != null ? this.content.length : -1); } + @Override public long getContentLengthLong() { return getContentLength(); } @@ -475,28 +476,25 @@ public class MockHttpServletRequest implements HttpServletRequest { *

If there are already one or more values registered for the given * parameter name, they will be replaced. */ - public void setParameter(String name, String[] values) { + public void setParameter(String name, String... values) { Assert.notNull(name, "Parameter name must not be null"); this.parameters.put(name, values); } /** - * Sets all provided parameters replacing any existing + * Set all provided parameters replacing any existing * values for the provided parameter names. To add without replacing * existing values, use {@link #addParameters(java.util.Map)}. */ - @SuppressWarnings("rawtypes") - public void setParameters(Map params) { + public void setParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); - for (Object key : params.keySet()) { - Assert.isInstanceOf(String.class, key, - "Parameter map key must be of type [" + String.class.getName() + "]"); + for (String key : params.keySet()) { Object value = params.get(key); if (value instanceof String) { - this.setParameter((String) key, (String) value); + setParameter(key, (String) value); } else if (value instanceof String[]) { - this.setParameter((String) key, (String[]) value); + setParameter(key, (String[]) value); } else { throw new IllegalArgumentException( @@ -519,7 +517,7 @@ public class MockHttpServletRequest implements HttpServletRequest { *

If there are already one or more values registered for the given * parameter name, the given values will be added to the end of the list. */ - public void addParameter(String name, String[] values) { + public void addParameter(String name, String... values) { Assert.notNull(name, "Parameter name must not be null"); String[] oldArr = this.parameters.get(name); if (oldArr != null) { @@ -538,18 +536,15 @@ public class MockHttpServletRequest implements HttpServletRequest { * existing values. To replace existing values, use * {@link #setParameters(java.util.Map)}. */ - @SuppressWarnings("rawtypes") - public void addParameters(Map params) { + public void addParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); - for (Object key : params.keySet()) { - Assert.isInstanceOf(String.class, key, - "Parameter map key must be of type [" + String.class.getName() + "]"); + for (String key : params.keySet()) { Object value = params.get(key); if (value instanceof String) { - this.addParameter((String) key, (String) value); + this.addParameter(key, (String) value); } else if (value instanceof String[]) { - this.addParameter((String) key, (String[]) value); + this.addParameter(key, (String[]) value); } else { throw new IllegalArgumentException("Parameter map value must be single value " + @@ -929,14 +924,14 @@ public class MockHttpServletRequest implements HttpServletRequest { * @see #getDateHeader */ public void addHeader(String name, Object value) { - if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name)) { - setContentType((String) value); - return; + if (CONTENT_TYPE_HEADER.equalsIgnoreCase(name) && !this.headers.containsKey(CONTENT_TYPE_HEADER)) { + setContentType(value.toString()); + } + else { + doAddHeaderValue(name, value, false); } - doAddHeaderValue(name, value, false); } - @SuppressWarnings("rawtypes") private void doAddHeaderValue(String name, Object value, boolean replace) { HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); Assert.notNull(value, "Header value must not be null");