|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2023 the original author or authors. |
| 2 | + * Copyright 2002-2024 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
17 | 17 | package org.springframework.test.web.servlet.request;
|
18 | 18 |
|
19 | 19 | import java.io.ByteArrayInputStream;
|
| 20 | +import java.io.ByteArrayOutputStream; |
20 | 21 | import java.io.IOException;
|
21 | 22 | import java.io.InputStream;
|
| 23 | +import java.io.OutputStream; |
22 | 24 | import java.net.URI;
|
23 | 25 | import java.nio.charset.Charset;
|
24 | 26 | import java.nio.charset.StandardCharsets;
|
|
40 | 42 | import org.springframework.http.HttpHeaders;
|
41 | 43 | import org.springframework.http.HttpInputMessage;
|
42 | 44 | import org.springframework.http.HttpMethod;
|
| 45 | +import org.springframework.http.HttpOutputMessage; |
43 | 46 | import org.springframework.http.MediaType;
|
44 | 47 | import org.springframework.http.converter.FormHttpMessageConverter;
|
45 | 48 | import org.springframework.lang.Nullable;
|
@@ -121,6 +124,8 @@ public class MockHttpServletRequestBuilder
|
121 | 124 |
|
122 | 125 | private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
123 | 126 |
|
| 127 | + private final MultiValueMap<String, String> formFields = new LinkedMultiValueMap<>(); |
| 128 | + |
124 | 129 | private final List<Cookie> cookies = new ArrayList<>();
|
125 | 130 |
|
126 | 131 | private final List<Locale> locales = new ArrayList<>();
|
@@ -422,6 +427,30 @@ public MockHttpServletRequestBuilder queryParams(MultiValueMap<String, String> p
|
422 | 427 | return this;
|
423 | 428 | }
|
424 | 429 |
|
| 430 | + /** |
| 431 | + * Appends the given value(s) to the given form field and also add to the |
| 432 | + * {@link #param(String, String...) request parameters} map. |
| 433 | + * @param name the field name |
| 434 | + * @param values one or more values |
| 435 | + * @since 6.1.7 |
| 436 | + */ |
| 437 | + public MockHttpServletRequestBuilder formField(String name, String... values) { |
| 438 | + param(name, values); |
| 439 | + this.formFields.addAll(name, Arrays.asList(values)); |
| 440 | + return this; |
| 441 | + } |
| 442 | + |
| 443 | + /** |
| 444 | + * Variant of {@link #formField(String, String...)} with a {@link MultiValueMap}. |
| 445 | + * @param formFields the form fields to add |
| 446 | + * @since 6.1.7 |
| 447 | + */ |
| 448 | + public MockHttpServletRequestBuilder formFields(MultiValueMap<String, String> formFields) { |
| 449 | + params(formFields); |
| 450 | + this.formFields.addAll(formFields); |
| 451 | + return this; |
| 452 | + } |
| 453 | + |
425 | 454 | /**
|
426 | 455 | * Add the given cookies to the request. Cookies are always added.
|
427 | 456 | * @param cookies the cookies to add
|
@@ -629,6 +658,12 @@ public Object merge(@Nullable Object parent) {
|
629 | 658 | this.queryParams.put(paramName, entry.getValue());
|
630 | 659 | }
|
631 | 660 | }
|
| 661 | + for (Map.Entry<String, List<String>> entry : parentBuilder.formFields.entrySet()) { |
| 662 | + String paramName = entry.getKey(); |
| 663 | + if (!this.formFields.containsKey(paramName)) { |
| 664 | + this.formFields.put(paramName, entry.getValue()); |
| 665 | + } |
| 666 | + } |
632 | 667 | for (Cookie cookie : parentBuilder.cookies) {
|
633 | 668 | if (!containsCookie(cookie)) {
|
634 | 669 | this.cookies.add(cookie);
|
@@ -744,6 +779,24 @@ public final MockHttpServletRequest buildRequest(ServletContext servletContext)
|
744 | 779 | }
|
745 | 780 | });
|
746 | 781 |
|
| 782 | + if (!this.formFields.isEmpty()) { |
| 783 | + if (this.content != null && this.content.length > 0) { |
| 784 | + throw new IllegalStateException("Could not write form data with an existing body"); |
| 785 | + } |
| 786 | + Charset charset = (this.characterEncoding != null |
| 787 | + ? Charset.forName(this.characterEncoding) : StandardCharsets.UTF_8); |
| 788 | + MediaType mediaType = (request.getContentType() != null |
| 789 | + ? MediaType.parseMediaType(request.getContentType()) |
| 790 | + : new MediaType(MediaType.APPLICATION_FORM_URLENCODED, charset)); |
| 791 | + if (!mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) { |
| 792 | + throw new IllegalStateException("Invalid content type: '" + mediaType |
| 793 | + + "' is not compatible with '" + MediaType.APPLICATION_FORM_URLENCODED + "'"); |
| 794 | + } |
| 795 | + request.setContent(writeFormData(mediaType, charset)); |
| 796 | + if (request.getContentType() == null) { |
| 797 | + request.setContentType(mediaType.toString()); |
| 798 | + } |
| 799 | + } |
747 | 800 | if (this.content != null && this.content.length > 0) {
|
748 | 801 | String requestContentType = request.getContentType();
|
749 | 802 | if (requestContentType != null) {
|
@@ -820,6 +873,32 @@ private void addRequestParams(MockHttpServletRequest request, MultiValueMap<Stri
|
820 | 873 | }));
|
821 | 874 | }
|
822 | 875 |
|
| 876 | + private byte[] writeFormData(MediaType mediaType, Charset charset) { |
| 877 | + ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| 878 | + HttpOutputMessage message = new HttpOutputMessage() { |
| 879 | + @Override |
| 880 | + public OutputStream getBody() { |
| 881 | + return out; |
| 882 | + } |
| 883 | + |
| 884 | + @Override |
| 885 | + public HttpHeaders getHeaders() { |
| 886 | + HttpHeaders headers = new HttpHeaders(); |
| 887 | + headers.setContentType(mediaType); |
| 888 | + return headers; |
| 889 | + } |
| 890 | + }; |
| 891 | + try { |
| 892 | + FormHttpMessageConverter messageConverter = new FormHttpMessageConverter(); |
| 893 | + messageConverter.setCharset(charset); |
| 894 | + messageConverter.write(this.formFields, mediaType, message); |
| 895 | + return out.toByteArray(); |
| 896 | + } |
| 897 | + catch (IOException ex) { |
| 898 | + throw new IllegalStateException("Failed to write form data to request body", ex); |
| 899 | + } |
| 900 | + } |
| 901 | + |
823 | 902 | private MultiValueMap<String, String> parseFormData(MediaType mediaType) {
|
824 | 903 | HttpInputMessage message = new HttpInputMessage() {
|
825 | 904 | @Override
|
|
0 commit comments