001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.web.servlet.request; 018 019import java.io.ByteArrayInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.UnsupportedEncodingException; 023import java.net.URI; 024import java.nio.charset.Charset; 025import java.security.Principal; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.LinkedHashMap; 029import java.util.List; 030import java.util.Locale; 031import java.util.Map; 032import java.util.Map.Entry; 033import javax.servlet.ServletContext; 034import javax.servlet.ServletRequest; 035import javax.servlet.http.Cookie; 036 037import org.springframework.beans.Mergeable; 038import org.springframework.beans.factory.NoSuchBeanDefinitionException; 039import org.springframework.http.HttpHeaders; 040import org.springframework.http.HttpInputMessage; 041import org.springframework.http.HttpMethod; 042import org.springframework.http.MediaType; 043import org.springframework.http.converter.FormHttpMessageConverter; 044import org.springframework.mock.web.MockHttpServletRequest; 045import org.springframework.mock.web.MockHttpServletResponse; 046import org.springframework.mock.web.MockHttpSession; 047import org.springframework.test.web.servlet.MockMvc; 048import org.springframework.util.Assert; 049import org.springframework.util.LinkedMultiValueMap; 050import org.springframework.util.MultiValueMap; 051import org.springframework.util.ObjectUtils; 052import org.springframework.util.StringUtils; 053import org.springframework.web.context.WebApplicationContext; 054import org.springframework.web.context.support.WebApplicationContextUtils; 055import org.springframework.web.servlet.DispatcherServlet; 056import org.springframework.web.servlet.FlashMap; 057import org.springframework.web.servlet.FlashMapManager; 058import org.springframework.web.servlet.support.SessionFlashMapManager; 059import org.springframework.web.util.UriComponentsBuilder; 060import org.springframework.web.util.UriUtils; 061import org.springframework.web.util.UrlPathHelper; 062 063/** 064 * Default builder for {@link MockHttpServletRequest} required as input to 065 * perform requests in {@link MockMvc}. 066 * 067 * <p>Application tests will typically access this builder through the static 068 * factory methods in {@link MockMvcRequestBuilders}. 069 * 070 * <p>This class is not open for extension. To apply custom initialization to 071 * the created {@code MockHttpServletRequest}, please use the 072 * {@link #with(RequestPostProcessor)} extension point. 073 * 074 * @author Rossen Stoyanchev 075 * @author Juergen Hoeller 076 * @author Arjen Poutsma 077 * @author Sam Brannen 078 * @author Kamill Sokol 079 * @since 3.2 080 */ 081public class MockHttpServletRequestBuilder 082 implements ConfigurableSmartRequestBuilder<MockHttpServletRequestBuilder>, Mergeable { 083 084 private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); 085 086 087 private final String method; 088 089 private final URI url; 090 091 private String contextPath = ""; 092 093 private String servletPath = ""; 094 095 private String pathInfo = ""; 096 097 private Boolean secure; 098 099 private Principal principal; 100 101 private MockHttpSession session; 102 103 private String characterEncoding; 104 105 private byte[] content; 106 107 private String contentType; 108 109 private final MultiValueMap<String, Object> headers = new LinkedMultiValueMap<String, Object>(); 110 111 private final MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>(); 112 113 private final List<Cookie> cookies = new ArrayList<Cookie>(); 114 115 private final List<Locale> locales = new ArrayList<Locale>(); 116 117 private final Map<String, Object> requestAttributes = new LinkedHashMap<String, Object>(); 118 119 private final Map<String, Object> sessionAttributes = new LinkedHashMap<String, Object>(); 120 121 private final Map<String, Object> flashAttributes = new LinkedHashMap<String, Object>(); 122 123 private final List<RequestPostProcessor> postProcessors = new ArrayList<RequestPostProcessor>(); 124 125 126 /** 127 * Package private constructor. To get an instance, use static factory 128 * methods in {@link MockMvcRequestBuilders}. 129 * <p>Although this class cannot be extended, additional ways to initialize 130 * the {@code MockHttpServletRequest} can be plugged in via 131 * {@link #with(RequestPostProcessor)}. 132 * @param httpMethod the HTTP method (GET, POST, etc) 133 * @param url a URL template; the resulting URL will be encoded 134 * @param vars zero or more URI variables 135 */ 136 MockHttpServletRequestBuilder(HttpMethod httpMethod, String url, Object... vars) { 137 this(httpMethod.name(), UriComponentsBuilder.fromUriString(url).buildAndExpand(vars).encode().toUri()); 138 } 139 140 /** 141 * Alternative to {@link #MockHttpServletRequestBuilder(HttpMethod, String, Object...)} 142 * with a pre-built URI. 143 * @param httpMethod the HTTP method (GET, POST, etc) 144 * @param url the URL 145 * @since 4.0.3 146 */ 147 MockHttpServletRequestBuilder(HttpMethod httpMethod, URI url) { 148 this(httpMethod.name(), url); 149 } 150 151 /** 152 * Alternative constructor for custom HTTP methods. 153 * @param httpMethod the HTTP method (GET, POST, etc) 154 * @param url the URL 155 * @since 4.3 156 */ 157 MockHttpServletRequestBuilder(String httpMethod, URI url) { 158 Assert.notNull(httpMethod, "'httpMethod' is required"); 159 Assert.notNull(url, "'url' is required"); 160 this.method = httpMethod; 161 this.url = url; 162 } 163 164 165 /** 166 * Specify the portion of the requestURI that represents the context path. 167 * The context path, if specified, must match to the start of the request URI. 168 * <p>In most cases, tests can be written by omitting the context path from 169 * the requestURI. This is because most applications don't actually depend 170 * on the name under which they're deployed. If specified here, the context 171 * path must start with a "/" and must not end with a "/". 172 * @see javax.servlet.http.HttpServletRequest#getContextPath() 173 */ 174 public MockHttpServletRequestBuilder contextPath(String contextPath) { 175 if (StringUtils.hasText(contextPath)) { 176 Assert.isTrue(contextPath.startsWith("/"), "Context path must start with a '/'"); 177 Assert.isTrue(!contextPath.endsWith("/"), "Context path must not end with a '/'"); 178 } 179 this.contextPath = (contextPath != null ? contextPath : ""); 180 return this; 181 } 182 183 /** 184 * Specify the portion of the requestURI that represents the path to which 185 * the Servlet is mapped. This is typically a portion of the requestURI 186 * after the context path. 187 * <p>In most cases, tests can be written by omitting the servlet path from 188 * the requestURI. This is because most applications don't actually depend 189 * on the prefix to which a servlet is mapped. For example if a Servlet is 190 * mapped to {@code "/main/*"}, tests can be written with the requestURI 191 * {@code "/accounts/1"} as opposed to {@code "/main/accounts/1"}. 192 * If specified here, the servletPath must start with a "/" and must not 193 * end with a "/". 194 * @see javax.servlet.http.HttpServletRequest#getServletPath() 195 */ 196 public MockHttpServletRequestBuilder servletPath(String servletPath) { 197 if (StringUtils.hasText(servletPath)) { 198 Assert.isTrue(servletPath.startsWith("/"), "Servlet path must start with a '/'"); 199 Assert.isTrue(!servletPath.endsWith("/"), "Servlet path must not end with a '/'"); 200 } 201 this.servletPath = (servletPath != null ? servletPath : ""); 202 return this; 203 } 204 205 /** 206 * Specify the portion of the requestURI that represents the pathInfo. 207 * <p>If left unspecified (recommended), the pathInfo will be automatically derived 208 * by removing the contextPath and the servletPath from the requestURI and using any 209 * remaining part. If specified here, the pathInfo must start with a "/". 210 * <p>If specified, the pathInfo will be used as-is. 211 * @see javax.servlet.http.HttpServletRequest#getPathInfo() 212 */ 213 public MockHttpServletRequestBuilder pathInfo(String pathInfo) { 214 if (StringUtils.hasText(pathInfo)) { 215 Assert.isTrue(pathInfo.startsWith("/"), "Path info must start with a '/'"); 216 } 217 this.pathInfo = pathInfo; 218 return this; 219 } 220 221 /** 222 * Set the secure property of the {@link ServletRequest} indicating use of a 223 * secure channel, such as HTTPS. 224 * @param secure whether the request is using a secure channel 225 */ 226 public MockHttpServletRequestBuilder secure(boolean secure){ 227 this.secure = secure; 228 return this; 229 } 230 231 /** 232 * Set the character encoding of the request. 233 * @param encoding the character encoding 234 */ 235 public MockHttpServletRequestBuilder characterEncoding(String encoding) { 236 this.characterEncoding = encoding; 237 return this; 238 } 239 240 /** 241 * Set the request body. 242 * @param content the body content 243 */ 244 public MockHttpServletRequestBuilder content(byte[] content) { 245 this.content = content; 246 return this; 247 } 248 249 /** 250 * Set the request body as a UTF-8 String. 251 * @param content the body content 252 */ 253 public MockHttpServletRequestBuilder content(String content) { 254 this.content = content.getBytes(UTF8_CHARSET); 255 return this; 256 } 257 258 /** 259 * Set the 'Content-Type' header of the request. 260 * @param contentType the content type 261 */ 262 public MockHttpServletRequestBuilder contentType(MediaType contentType) { 263 Assert.notNull(contentType, "'contentType' must not be null"); 264 this.contentType = contentType.toString(); 265 return this; 266 } 267 268 /** 269 * Set the 'Content-Type' header of the request. 270 * @param contentType the content type 271 * @since 4.1.2 272 */ 273 public MockHttpServletRequestBuilder contentType(String contentType) { 274 this.contentType = MediaType.parseMediaType(contentType).toString(); 275 return this; 276 } 277 278 /** 279 * Set the 'Accept' header to the given media type(s). 280 * @param mediaTypes one or more media types 281 */ 282 public MockHttpServletRequestBuilder accept(MediaType... mediaTypes) { 283 Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty"); 284 this.headers.set("Accept", MediaType.toString(Arrays.asList(mediaTypes))); 285 return this; 286 } 287 288 /** 289 * Set the 'Accept' header to the given media type(s). 290 * @param mediaTypes one or more media types 291 */ 292 public MockHttpServletRequestBuilder accept(String... mediaTypes) { 293 Assert.notEmpty(mediaTypes, "'mediaTypes' must not be empty"); 294 List<MediaType> result = new ArrayList<MediaType>(mediaTypes.length); 295 for (String mediaType : mediaTypes) { 296 result.add(MediaType.parseMediaType(mediaType)); 297 } 298 this.headers.set("Accept", MediaType.toString(result)); 299 return this; 300 } 301 302 /** 303 * Add a header to the request. Values are always added. 304 * @param name the header name 305 * @param values one or more header values 306 */ 307 public MockHttpServletRequestBuilder header(String name, Object... values) { 308 addToMultiValueMap(this.headers, name, values); 309 return this; 310 } 311 312 /** 313 * Add all headers to the request. Values are always added. 314 * @param httpHeaders the headers and values to add 315 */ 316 public MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders) { 317 for (String name : httpHeaders.keySet()) { 318 Object[] values = ObjectUtils.toObjectArray(httpHeaders.get(name).toArray()); 319 addToMultiValueMap(this.headers, name, values); 320 } 321 return this; 322 } 323 324 /** 325 * Add a request parameter to the {@link MockHttpServletRequest}. 326 * <p>If called more than once, new values get added to existing ones. 327 * @param name the parameter name 328 * @param values one or more values 329 */ 330 public MockHttpServletRequestBuilder param(String name, String... values) { 331 addToMultiValueMap(this.parameters, name, values); 332 return this; 333 } 334 335 /** 336 * Add a map of request parameters to the {@link MockHttpServletRequest}, 337 * for example when testing a form submission. 338 * <p>If called more than once, new values get added to existing ones. 339 * @param params the parameters to add 340 * @since 4.2.4 341 */ 342 public MockHttpServletRequestBuilder params(MultiValueMap<String, String> params) { 343 for (String name : params.keySet()) { 344 for (String value : params.get(name)) { 345 this.parameters.add(name, value); 346 } 347 } 348 return this; 349 } 350 351 /** 352 * Add the given cookies to the request. Cookies are always added. 353 * @param cookies the cookies to add 354 */ 355 public MockHttpServletRequestBuilder cookie(Cookie... cookies) { 356 Assert.notEmpty(cookies, "'cookies' must not be empty"); 357 this.cookies.addAll(Arrays.asList(cookies)); 358 return this; 359 } 360 361 /** 362 * Add the specified locales as preferred request locales. 363 * @param locales the locales to add 364 * @since 4.3.6 365 * @see #locale(Locale) 366 */ 367 public MockHttpServletRequestBuilder locale(Locale... locales) { 368 Assert.notEmpty(locales, "'locales' must not be empty"); 369 this.locales.addAll(Arrays.asList(locales)); 370 return this; 371 } 372 373 /** 374 * Set the locale of the request, overriding any previous locales. 375 * @param locale the locale, or {@code null} to reset it 376 * @see #locale(Locale...) 377 */ 378 public MockHttpServletRequestBuilder locale(Locale locale) { 379 this.locales.clear(); 380 if (locale != null) { 381 this.locales.add(locale); 382 } 383 return this; 384 } 385 386 /** 387 * Set a request attribute. 388 * @param name the attribute name 389 * @param value the attribute value 390 */ 391 public MockHttpServletRequestBuilder requestAttr(String name, Object value) { 392 addToMap(this.requestAttributes, name, value); 393 return this; 394 } 395 396 /** 397 * Set a session attribute. 398 * @param name the session attribute name 399 * @param value the session attribute value 400 */ 401 public MockHttpServletRequestBuilder sessionAttr(String name, Object value) { 402 addToMap(this.sessionAttributes, name, value); 403 return this; 404 } 405 406 /** 407 * Set session attributes. 408 * @param sessionAttributes the session attributes 409 */ 410 public MockHttpServletRequestBuilder sessionAttrs(Map<String, Object> sessionAttributes) { 411 Assert.notEmpty(sessionAttributes, "'sessionAttributes' must not be empty"); 412 for (String name : sessionAttributes.keySet()) { 413 sessionAttr(name, sessionAttributes.get(name)); 414 } 415 return this; 416 } 417 418 /** 419 * Set an "input" flash attribute. 420 * @param name the flash attribute name 421 * @param value the flash attribute value 422 */ 423 public MockHttpServletRequestBuilder flashAttr(String name, Object value) { 424 addToMap(this.flashAttributes, name, value); 425 return this; 426 } 427 428 /** 429 * Set flash attributes. 430 * @param flashAttributes the flash attributes 431 */ 432 public MockHttpServletRequestBuilder flashAttrs(Map<String, Object> flashAttributes) { 433 Assert.notEmpty(flashAttributes, "'flashAttributes' must not be empty"); 434 for (String name : flashAttributes.keySet()) { 435 flashAttr(name, flashAttributes.get(name)); 436 } 437 return this; 438 } 439 440 /** 441 * Set the HTTP session to use, possibly re-used across requests. 442 * <p>Individual attributes provided via {@link #sessionAttr(String, Object)} 443 * override the content of the session provided here. 444 * @param session the HTTP session 445 */ 446 public MockHttpServletRequestBuilder session(MockHttpSession session) { 447 Assert.notNull(session, "'session' must not be null"); 448 this.session = session; 449 return this; 450 } 451 452 /** 453 * Set the principal of the request. 454 * @param principal the principal 455 */ 456 public MockHttpServletRequestBuilder principal(Principal principal) { 457 Assert.notNull(principal, "'principal' must not be null"); 458 this.principal = principal; 459 return this; 460 } 461 462 /** 463 * An extension point for further initialization of {@link MockHttpServletRequest} 464 * in ways not built directly into the {@code MockHttpServletRequestBuilder}. 465 * Implementation of this interface can have builder-style methods themselves 466 * and be made accessible through static factory methods. 467 * @param postProcessor a post-processor to add 468 */ 469 @Override 470 public MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor) { 471 Assert.notNull(postProcessor, "postProcessor is required"); 472 this.postProcessors.add(postProcessor); 473 return this; 474 } 475 476 477 /** 478 * {@inheritDoc} 479 * @return always returns {@code true}. 480 */ 481 @Override 482 public boolean isMergeEnabled() { 483 return true; 484 } 485 486 /** 487 * Merges the properties of the "parent" RequestBuilder accepting values 488 * only if not already set in "this" instance. 489 * @param parent the parent {@code RequestBuilder} to inherit properties from 490 * @return the result of the merge 491 */ 492 @Override 493 public Object merge(Object parent) { 494 if (parent == null) { 495 return this; 496 } 497 if (!(parent instanceof MockHttpServletRequestBuilder)) { 498 throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]"); 499 } 500 MockHttpServletRequestBuilder parentBuilder = (MockHttpServletRequestBuilder) parent; 501 502 if (!StringUtils.hasText(this.contextPath)) { 503 this.contextPath = parentBuilder.contextPath; 504 } 505 if (!StringUtils.hasText(this.servletPath)) { 506 this.servletPath = parentBuilder.servletPath; 507 } 508 if ("".equals(this.pathInfo)) { 509 this.pathInfo = parentBuilder.pathInfo; 510 } 511 512 if (this.secure == null) { 513 this.secure = parentBuilder.secure; 514 } 515 if (this.principal == null) { 516 this.principal = parentBuilder.principal; 517 } 518 if (this.session == null) { 519 this.session = parentBuilder.session; 520 } 521 522 if (this.characterEncoding == null) { 523 this.characterEncoding = parentBuilder.characterEncoding; 524 } 525 if (this.content == null) { 526 this.content = parentBuilder.content; 527 } 528 if (this.contentType == null) { 529 this.contentType = parentBuilder.contentType; 530 } 531 532 for (String headerName : parentBuilder.headers.keySet()) { 533 if (!this.headers.containsKey(headerName)) { 534 this.headers.put(headerName, parentBuilder.headers.get(headerName)); 535 } 536 } 537 for (String paramName : parentBuilder.parameters.keySet()) { 538 if (!this.parameters.containsKey(paramName)) { 539 this.parameters.put(paramName, parentBuilder.parameters.get(paramName)); 540 } 541 } 542 for (Cookie cookie : parentBuilder.cookies) { 543 if (!containsCookie(cookie)) { 544 this.cookies.add(cookie); 545 } 546 } 547 for (Locale locale : parentBuilder.locales) { 548 if (!this.locales.contains(locale)) { 549 this.locales.add(locale); 550 } 551 } 552 553 for (String attributeName : parentBuilder.requestAttributes.keySet()) { 554 if (!this.requestAttributes.containsKey(attributeName)) { 555 this.requestAttributes.put(attributeName, parentBuilder.requestAttributes.get(attributeName)); 556 } 557 } 558 for (String attributeName : parentBuilder.sessionAttributes.keySet()) { 559 if (!this.sessionAttributes.containsKey(attributeName)) { 560 this.sessionAttributes.put(attributeName, parentBuilder.sessionAttributes.get(attributeName)); 561 } 562 } 563 for (String attributeName : parentBuilder.flashAttributes.keySet()) { 564 if (!this.flashAttributes.containsKey(attributeName)) { 565 this.flashAttributes.put(attributeName, parentBuilder.flashAttributes.get(attributeName)); 566 } 567 } 568 569 this.postProcessors.addAll(0, parentBuilder.postProcessors); 570 571 return this; 572 } 573 574 private boolean containsCookie(Cookie cookie) { 575 for (Cookie cookieToCheck : this.cookies) { 576 if (ObjectUtils.nullSafeEquals(cookieToCheck.getName(), cookie.getName())) { 577 return true; 578 } 579 } 580 return false; 581 } 582 583 /** 584 * Build a {@link MockHttpServletRequest}. 585 */ 586 @Override 587 public final MockHttpServletRequest buildRequest(ServletContext servletContext) { 588 MockHttpServletRequest request = createServletRequest(servletContext); 589 590 request.setAsyncSupported(true); 591 request.setMethod(this.method); 592 593 String requestUri = this.url.getRawPath(); 594 request.setRequestURI(requestUri); 595 596 if (this.url.getScheme() != null) { 597 request.setScheme(this.url.getScheme()); 598 } 599 if (this.url.getHost() != null) { 600 request.setServerName(this.url.getHost()); 601 } 602 if (this.url.getPort() != -1) { 603 request.setServerPort(this.url.getPort()); 604 } 605 606 updatePathRequestProperties(request, requestUri); 607 608 if (this.secure != null) { 609 request.setSecure(this.secure); 610 } 611 if (this.principal != null) { 612 request.setUserPrincipal(this.principal); 613 } 614 if (this.session != null) { 615 request.setSession(this.session); 616 } 617 618 request.setCharacterEncoding(this.characterEncoding); 619 request.setContent(this.content); 620 request.setContentType(this.contentType); 621 622 for (String name : this.headers.keySet()) { 623 for (Object value : this.headers.get(name)) { 624 request.addHeader(name, value); 625 } 626 } 627 628 if (this.url.getRawQuery() != null) { 629 request.setQueryString(this.url.getRawQuery()); 630 } 631 addRequestParams(request, UriComponentsBuilder.fromUri(this.url).build().getQueryParams()); 632 633 for (String name : this.parameters.keySet()) { 634 for (String value : this.parameters.get(name)) { 635 request.addParameter(name, value); 636 } 637 } 638 639 if (this.content != null && this.content.length > 0) { 640 String requestContentType = request.getContentType(); 641 if (requestContentType != null) { 642 MediaType mediaType = MediaType.parseMediaType(requestContentType); 643 if (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)) { 644 addRequestParams(request, parseFormData(mediaType)); 645 } 646 } 647 } 648 649 if (!ObjectUtils.isEmpty(this.cookies)) { 650 request.setCookies(this.cookies.toArray(new Cookie[this.cookies.size()])); 651 } 652 if (!ObjectUtils.isEmpty(this.locales)) { 653 request.setPreferredLocales(this.locales); 654 } 655 656 for (String name : this.requestAttributes.keySet()) { 657 request.setAttribute(name, this.requestAttributes.get(name)); 658 } 659 for (String name : this.sessionAttributes.keySet()) { 660 request.getSession().setAttribute(name, this.sessionAttributes.get(name)); 661 } 662 663 FlashMap flashMap = new FlashMap(); 664 flashMap.putAll(this.flashAttributes); 665 FlashMapManager flashMapManager = getFlashMapManager(request); 666 flashMapManager.saveOutputFlashMap(flashMap, request, new MockHttpServletResponse()); 667 668 return request; 669 } 670 671 /** 672 * Create a new {@link MockHttpServletRequest} based on the supplied 673 * {@code ServletContext}. 674 * <p>Can be overridden in subclasses. 675 */ 676 protected MockHttpServletRequest createServletRequest(ServletContext servletContext) { 677 return new MockHttpServletRequest(servletContext); 678 } 679 680 /** 681 * Update the contextPath, servletPath, and pathInfo of the request. 682 */ 683 private void updatePathRequestProperties(MockHttpServletRequest request, String requestUri) { 684 if (!requestUri.startsWith(this.contextPath)) { 685 throw new IllegalArgumentException( 686 "Request URI [" + requestUri + "] does not start with context path [" + this.contextPath + "]"); 687 } 688 request.setContextPath(this.contextPath); 689 request.setServletPath(this.servletPath); 690 691 if ("".equals(this.pathInfo)) { 692 if (!requestUri.startsWith(this.contextPath + this.servletPath)) { 693 throw new IllegalArgumentException( 694 "Invalid servlet path [" + this.servletPath + "] for request URI [" + requestUri + "]"); 695 } 696 String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length()); 697 this.pathInfo = (StringUtils.hasText(extraPath) ? 698 UrlPathHelper.defaultInstance.decodeRequestString(request, extraPath) : null); 699 } 700 request.setPathInfo(this.pathInfo); 701 } 702 703 private void addRequestParams(MockHttpServletRequest request, MultiValueMap<String, String> map) { 704 try { 705 for (Entry<String, List<String>> entry : map.entrySet()) { 706 for (String value : entry.getValue()) { 707 value = (value != null) ? UriUtils.decode(value, "UTF-8") : null; 708 request.addParameter(UriUtils.decode(entry.getKey(), "UTF-8"), value); 709 } 710 } 711 } 712 catch (UnsupportedEncodingException ex) { 713 // shouldn't happen 714 } 715 } 716 717 private MultiValueMap<String, String> parseFormData(final MediaType mediaType) { 718 HttpInputMessage message = new HttpInputMessage() { 719 @Override 720 public InputStream getBody() throws IOException { 721 return new ByteArrayInputStream(content); 722 } 723 @Override 724 public HttpHeaders getHeaders() { 725 HttpHeaders headers = new HttpHeaders(); 726 headers.setContentType(mediaType); 727 return headers; 728 } 729 }; 730 731 try { 732 return new FormHttpMessageConverter().read(null, message); 733 } 734 catch (IOException ex) { 735 throw new IllegalStateException("Failed to parse form data in request body", ex); 736 } 737 } 738 739 private FlashMapManager getFlashMapManager(MockHttpServletRequest request) { 740 FlashMapManager flashMapManager = null; 741 try { 742 ServletContext servletContext = request.getServletContext(); 743 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); 744 flashMapManager = wac.getBean(DispatcherServlet.FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class); 745 } 746 catch (IllegalStateException ex) { 747 // ignore 748 } 749 catch (NoSuchBeanDefinitionException ex) { 750 // ignore 751 } 752 return (flashMapManager != null ? flashMapManager : new SessionFlashMapManager()); 753 } 754 755 @Override 756 public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { 757 for (RequestPostProcessor postProcessor : this.postProcessors) { 758 request = postProcessor.postProcessRequest(request); 759 if (request == null) { 760 throw new IllegalStateException( 761 "Post-processor [" + postProcessor.getClass().getName() + "] returned null"); 762 } 763 } 764 return request; 765 } 766 767 768 private static void addToMap(Map<String, Object> map, String name, Object value) { 769 Assert.hasLength(name, "'name' must not be empty"); 770 Assert.notNull(value, "'value' must not be null"); 771 map.put(name, value); 772 } 773 774 private static <T> void addToMultiValueMap(MultiValueMap<String, T> map, String name, T[] values) { 775 Assert.hasLength(name, "'name' must not be empty"); 776 Assert.notEmpty(values, "'values' must not be empty"); 777 for (T value : values) { 778 map.add(name, value); 779 } 780 } 781 782}