Add support for form data in MockHttpServletRequestBuilder
Closes gh-32757
This commit is contained in:
parent
5b1278d03c
commit
6250b64766
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
|
|
@ -17,8 +17,10 @@
|
|||
package org.springframework.test.web.servlet.request;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
|
@ -40,6 +42,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
|
@ -121,6 +124,8 @@ public class MockHttpServletRequestBuilder
|
|||
|
||||
private final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
|
||||
private final MultiValueMap<String, String> formFields = new LinkedMultiValueMap<>();
|
||||
|
||||
private final List<Cookie> cookies = new ArrayList<>();
|
||||
|
||||
private final List<Locale> locales = new ArrayList<>();
|
||||
|
|
@ -422,6 +427,30 @@ public class MockHttpServletRequestBuilder
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given value(s) to the given form field and also add to the
|
||||
* {@link #param(String, String...) request parameters} map.
|
||||
* @param name the field name
|
||||
* @param values one or more values
|
||||
* @since 6.1.7
|
||||
*/
|
||||
public MockHttpServletRequestBuilder formField(String name, String... values) {
|
||||
param(name, values);
|
||||
this.formFields.addAll(name, Arrays.asList(values));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #formField(String, String...)} with a {@link MultiValueMap}.
|
||||
* @param formFields the form fields to add
|
||||
* @since 6.1.7
|
||||
*/
|
||||
public MockHttpServletRequestBuilder formFields(MultiValueMap<String, String> formFields) {
|
||||
params(formFields);
|
||||
this.formFields.addAll(formFields);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given cookies to the request. Cookies are always added.
|
||||
* @param cookies the cookies to add
|
||||
|
|
@ -629,6 +658,12 @@ public class MockHttpServletRequestBuilder
|
|||
this.queryParams.put(paramName, entry.getValue());
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, List<String>> entry : parentBuilder.formFields.entrySet()) {
|
||||
String paramName = entry.getKey();
|
||||
if (!this.formFields.containsKey(paramName)) {
|
||||
this.formFields.put(paramName, entry.getValue());
|
||||
}
|
||||
}
|
||||
for (Cookie cookie : parentBuilder.cookies) {
|
||||
if (!containsCookie(cookie)) {
|
||||
this.cookies.add(cookie);
|
||||
|
|
@ -744,6 +779,24 @@ public class MockHttpServletRequestBuilder
|
|||
}
|
||||
});
|
||||
|
||||
if (!this.formFields.isEmpty()) {
|
||||
if (this.content != null && this.content.length > 0) {
|
||||
throw new IllegalStateException("Could not write form data with an existing body");
|
||||
}
|
||||
Charset charset = (this.characterEncoding != null
|
||||
? Charset.forName(this.characterEncoding) : StandardCharsets.UTF_8);
|
||||
MediaType mediaType = (request.getContentType() != null
|
||||
? MediaType.parseMediaType(request.getContentType())
|
||||
: new MediaType(MediaType.APPLICATION_FORM_URLENCODED, charset));
|
||||
if (!mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) {
|
||||
throw new IllegalStateException("Invalid content type: '" + mediaType
|
||||
+ "' is not compatible with '" + MediaType.APPLICATION_FORM_URLENCODED + "'");
|
||||
}
|
||||
request.setContent(writeFormData(mediaType, charset));
|
||||
if (request.getContentType() == null) {
|
||||
request.setContentType(mediaType.toString());
|
||||
}
|
||||
}
|
||||
if (this.content != null && this.content.length > 0) {
|
||||
String requestContentType = request.getContentType();
|
||||
if (requestContentType != null) {
|
||||
|
|
@ -820,6 +873,32 @@ public class MockHttpServletRequestBuilder
|
|||
}));
|
||||
}
|
||||
|
||||
private byte[] writeFormData(MediaType mediaType, Charset charset) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
HttpOutputMessage message = new HttpOutputMessage() {
|
||||
@Override
|
||||
public OutputStream getBody() {
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(mediaType);
|
||||
return headers;
|
||||
}
|
||||
};
|
||||
try {
|
||||
FormHttpMessageConverter messageConverter = new FormHttpMessageConverter();
|
||||
messageConverter.setCharset(charset);
|
||||
messageConverter.write(this.formFields, mediaType, message);
|
||||
return out.toByteArray();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to write form data to request body", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> parseFormData(MediaType mediaType) {
|
||||
HttpInputMessage message = new HttpInputMessage() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.util.Map;
|
|||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import org.assertj.core.api.ThrowingConsumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
|
@ -47,6 +48,8 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
|||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.springframework.http.HttpMethod.GET;
|
||||
import static org.springframework.http.HttpMethod.POST;
|
||||
|
||||
|
|
@ -288,6 +291,70 @@ class MockHttpServletRequestBuilderTests {
|
|||
assertThat(request.getParameter("foo[1]")).isEqualTo("baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void formField() {
|
||||
this.builder = new MockHttpServletRequestBuilder(POST, "/");
|
||||
this.builder.formField("foo", "bar");
|
||||
this.builder.formField("foo", "baz");
|
||||
|
||||
MockHttpServletRequest request = this.builder.buildRequest(this.servletContext);
|
||||
|
||||
assertThat(request.getParameterMap().get("foo")).containsExactly("bar", "baz");
|
||||
assertThat(request).satisfies(hasFormData("foo=bar&foo=baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formFieldMap() {
|
||||
this.builder = new MockHttpServletRequestBuilder(POST, "/");
|
||||
MultiValueMap<String, String> formFields = new LinkedMultiValueMap<>();
|
||||
List<String> values = new ArrayList<>();
|
||||
values.add("bar");
|
||||
values.add("baz");
|
||||
formFields.put("foo", values);
|
||||
this.builder.formFields(formFields);
|
||||
|
||||
MockHttpServletRequest request = this.builder.buildRequest(this.servletContext);
|
||||
|
||||
assertThat(request.getParameterMap().get("foo")).containsExactly("bar", "baz");
|
||||
assertThat(request).satisfies(hasFormData("foo=bar&foo=baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formFieldsAreEncoded() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequestBuilder(POST, "/")
|
||||
.formField("name 1", "value 1").formField("name 2", "value A", "value B")
|
||||
.buildRequest(new MockServletContext());
|
||||
assertThat(request.getParameterMap()).containsOnly(
|
||||
entry("name 1", new String[] { "value 1" }),
|
||||
entry("name 2", new String[] { "value A", "value B" }));
|
||||
assertThat(request).satisfies(hasFormData("name+1=value+1&name+2=value+A&name+2=value+B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formFieldWithContent() {
|
||||
this.builder = new MockHttpServletRequestBuilder(POST, "/");
|
||||
this.builder.content("Should not have content");
|
||||
this.builder.formField("foo", "bar");
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.builder.buildRequest(this.servletContext))
|
||||
.withMessage("Could not write form data with an existing body");
|
||||
}
|
||||
|
||||
@Test
|
||||
void formFieldWithIncompatibleMediaType() {
|
||||
this.builder = new MockHttpServletRequestBuilder(POST, "/");
|
||||
this.builder.contentType(MediaType.TEXT_PLAIN);
|
||||
this.builder.formField("foo", "bar");
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.builder.buildRequest(this.servletContext))
|
||||
.withMessage("Invalid content type: 'text/plain' is not compatible with 'application/x-www-form-urlencoded'");
|
||||
}
|
||||
|
||||
private ThrowingConsumer<MockHttpServletRequest> hasFormData(String body) {
|
||||
return request -> {
|
||||
assertThat(request.getContentAsString()).isEqualTo(body);
|
||||
assertThat(request.getContentType()).isEqualTo("application/x-www-form-urlencoded;charset=UTF-8");
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestParameterFromQueryWithEncoding() {
|
||||
this.builder = new MockHttpServletRequestBuilder(GET, "/?foo={value}", "bar=baz");
|
||||
|
|
|
|||
Loading…
Reference in New Issue