All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.restdocs.mockmvc.MockMvcRequestConverter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2022 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.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.restdocs.mockmvc;

import java.io.IOException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.Part;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.restdocs.operation.ConversionException;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestFactory;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.OperationRequestPartFactory;
import org.springframework.restdocs.operation.RequestConverter;
import org.springframework.restdocs.operation.RequestCookie;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * A converter for creating an {@link OperationRequest} from a
 * {@link MockHttpServletRequest}.
 *
 * @author Andy Wilkinson
 * @author Clyde Stubbs
 */
class MockMvcRequestConverter implements RequestConverter {

	@Override
	public OperationRequest convert(MockHttpServletRequest mockRequest) {
		try {
			HttpHeaders headers = extractHeaders(mockRequest);
			List parts = extractParts(mockRequest);
			Collection cookies = extractCookies(mockRequest, headers);
			return new OperationRequestFactory().create(getRequestUri(mockRequest),
					HttpMethod.valueOf(mockRequest.getMethod()), getRequestContent(mockRequest, headers), headers,
					parts, cookies);
		}
		catch (Exception ex) {
			throw new ConversionException(ex);
		}
	}

	private URI getRequestUri(MockHttpServletRequest mockRequest) {
		String queryString = "";
		if (mockRequest.getQueryString() != null) {
			queryString = mockRequest.getQueryString();
		}
		else if ("GET".equals(mockRequest.getMethod()) || mockRequest.getContentLengthLong() > 0) {
			queryString = urlEncodedParameters(mockRequest);
		}
		StringBuffer requestUrlBuffer = mockRequest.getRequestURL();
		if (queryString.length() > 0) {
			requestUrlBuffer.append("?").append(queryString.toString());
		}
		return URI.create(requestUrlBuffer.toString());
	}

	private String urlEncodedParameters(MockHttpServletRequest mockRequest) {
		StringBuilder parameters = new StringBuilder();
		MultiValueMap queryParameters = parse(mockRequest.getQueryString());
		for (String name : IterableEnumeration.of(mockRequest.getParameterNames())) {
			if (!queryParameters.containsKey(name)) {
				String[] values = mockRequest.getParameterValues(name);
				if (values.length == 0) {
					append(parameters, name);
				}
				else {
					for (String value : values) {
						append(parameters, name, value);
					}
				}
			}
		}
		return parameters.toString();
	}

	private byte[] getRequestContent(MockHttpServletRequest mockRequest, HttpHeaders headers) {
		byte[] content = mockRequest.getContentAsByteArray();
		if ("GET".equals(mockRequest.getMethod())) {
			return content;
		}
		MediaType contentType = headers.getContentType();
		if (contentType == null || MediaType.APPLICATION_FORM_URLENCODED.includes(contentType)) {
			Map parameters = mockRequest.getParameterMap();
			if (!parameters.isEmpty() && (content == null || content.length == 0)) {
				StringBuilder contentBuilder = new StringBuilder();
				headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
				MultiValueMap queryParameters = parse(mockRequest.getQueryString());
				mockRequest.getParameterMap().forEach((name, values) -> {
					List queryParameterValues = queryParameters.get(name);
					if (values.length == 0) {
						if (queryParameterValues == null) {
							append(contentBuilder, name);
						}
					}
					else {
						for (String value : values) {
							if (queryParameterValues == null || !queryParameterValues.contains(value)) {
								append(contentBuilder, name, value);
							}
						}
					}
				});
				return contentBuilder.toString().getBytes(StandardCharsets.UTF_8);
			}
		}
		return content;
	}

	private Collection extractCookies(MockHttpServletRequest mockRequest, HttpHeaders headers) {
		if (mockRequest.getCookies() == null || mockRequest.getCookies().length == 0) {
			return Collections.emptyList();
		}
		List cookies = new ArrayList<>();
		for (jakarta.servlet.http.Cookie servletCookie : mockRequest.getCookies()) {
			cookies.add(new RequestCookie(servletCookie.getName(), servletCookie.getValue()));
		}
		headers.remove(HttpHeaders.COOKIE);
		return cookies;
	}

	private List extractParts(MockHttpServletRequest servletRequest)
			throws IOException, ServletException {
		List parts = new ArrayList<>();
		parts.addAll(extractServletRequestParts(servletRequest));
		if (servletRequest instanceof MockMultipartHttpServletRequest) {
			parts.addAll(extractMultipartRequestParts((MockMultipartHttpServletRequest) servletRequest));
		}
		return parts;
	}

	private List extractServletRequestParts(MockHttpServletRequest servletRequest)
			throws IOException, ServletException {
		List parts = new ArrayList<>();
		for (Part part : servletRequest.getParts()) {
			parts.add(createOperationRequestPart(part));
		}
		return parts;
	}

	private OperationRequestPart createOperationRequestPart(Part part) throws IOException {
		HttpHeaders partHeaders = extractHeaders(part);
		List contentTypeHeader = partHeaders.get(HttpHeaders.CONTENT_TYPE);
		if (part.getContentType() != null && contentTypeHeader == null) {
			partHeaders.setContentType(MediaType.parseMediaType(part.getContentType()));
		}
		return new OperationRequestPartFactory().create(part.getName(),
				StringUtils.hasText(part.getSubmittedFileName()) ? part.getSubmittedFileName() : null,
				FileCopyUtils.copyToByteArray(part.getInputStream()), partHeaders);
	}

	private List extractMultipartRequestParts(MockMultipartHttpServletRequest multipartRequest)
			throws IOException {
		List parts = new ArrayList<>();
		for (Entry> entry : multipartRequest.getMultiFileMap().entrySet()) {
			for (MultipartFile file : entry.getValue()) {
				parts.add(createOperationRequestPart(file));
			}
		}
		return parts;
	}

	private OperationRequestPart createOperationRequestPart(MultipartFile file) throws IOException {
		HttpHeaders partHeaders = new HttpHeaders();
		if (StringUtils.hasText(file.getContentType())) {
			partHeaders.setContentType(MediaType.parseMediaType(file.getContentType()));
		}
		return new OperationRequestPartFactory().create(file.getName(),
				StringUtils.hasText(file.getOriginalFilename()) ? file.getOriginalFilename() : null, file.getBytes(),
				partHeaders);
	}

	private HttpHeaders extractHeaders(Part part) {
		HttpHeaders partHeaders = new HttpHeaders();
		for (String headerName : part.getHeaderNames()) {
			for (String value : part.getHeaders(headerName)) {
				partHeaders.add(headerName, value);
			}
		}
		return partHeaders;
	}

	private HttpHeaders extractHeaders(MockHttpServletRequest servletRequest) {
		HttpHeaders headers = new HttpHeaders();
		for (String headerName : IterableEnumeration.of(servletRequest.getHeaderNames())) {
			for (String value : IterableEnumeration.of(servletRequest.getHeaders(headerName))) {
				headers.add(headerName, value);
			}
		}
		return headers;
	}

	private static void append(StringBuilder sb, String key) {
		append(sb, key, "");
	}

	private static void append(StringBuilder sb, String key, String value) {
		doAppend(sb, urlEncode(key) + "=" + urlEncode(value));
	}

	private static void doAppend(StringBuilder sb, String toAppend) {
		if (sb.length() > 0) {
			sb.append("&");
		}
		sb.append(toAppend);
	}

	private static String urlEncode(String s) {
		if (!StringUtils.hasLength(s)) {
			return "";
		}
		return URLEncoder.encode(s, StandardCharsets.UTF_8);
	}

	private static MultiValueMap parse(String query) {
		MultiValueMap parameters = new LinkedMultiValueMap<>();
		if (!StringUtils.hasLength(query)) {
			return parameters;
		}
		try (Scanner scanner = new Scanner(query)) {
			scanner.useDelimiter("&");
			while (scanner.hasNext()) {
				processParameter(scanner.next(), parameters);
			}
		}
		return parameters;
	}

	private static void processParameter(String parameter, MultiValueMap parameters) {
		String[] components = parameter.split("=");
		if (components.length > 0 && components.length < 3) {
			if (components.length == 2) {
				String name = components[0];
				String value = components[1];
				parameters.add(decode(name), decode(value));
			}
			else {
				List values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<>());
				values.add("");
			}
		}
		else {
			throw new IllegalArgumentException("The parameter '" + parameter + "' is malformed");
		}
	}

	private static String decode(String encoded) {
		return URLDecoder.decode(encoded, StandardCharsets.US_ASCII);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy