001/* 002 * Copyright 2002-2017 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.net.URI; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.List; 023 024import javax.servlet.ServletContext; 025import javax.servlet.http.Part; 026 027import org.springframework.http.HttpMethod; 028import org.springframework.http.MediaType; 029import org.springframework.lang.Nullable; 030import org.springframework.mock.web.MockHttpServletRequest; 031import org.springframework.mock.web.MockMultipartFile; 032import org.springframework.mock.web.MockMultipartHttpServletRequest; 033import org.springframework.util.Assert; 034import org.springframework.util.LinkedMultiValueMap; 035import org.springframework.util.MultiValueMap; 036import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest; 037 038/** 039 * Default builder for {@link MockMultipartHttpServletRequest}. 040 * 041 * @author Rossen Stoyanchev 042 * @author Arjen Poutsma 043 * @since 3.2 044 */ 045public class MockMultipartHttpServletRequestBuilder extends MockHttpServletRequestBuilder { 046 047 private final List<MockMultipartFile> files = new ArrayList<>(); 048 049 private final MultiValueMap<String, Part> parts = new LinkedMultiValueMap<>(); 050 051 052 /** 053 * Package-private constructor. Use static factory methods in 054 * {@link MockMvcRequestBuilders}. 055 * <p>For other ways to initialize a {@code MockMultipartHttpServletRequest}, 056 * see {@link #with(RequestPostProcessor)} and the 057 * {@link RequestPostProcessor} extension point. 058 * @param urlTemplate a URL template; the resulting URL will be encoded 059 * @param uriVariables zero or more URI variables 060 */ 061 MockMultipartHttpServletRequestBuilder(String urlTemplate, Object... uriVariables) { 062 super(HttpMethod.POST, urlTemplate, uriVariables); 063 super.contentType(MediaType.MULTIPART_FORM_DATA); 064 } 065 066 /** 067 * Package-private constructor. Use static factory methods in 068 * {@link MockMvcRequestBuilders}. 069 * <p>For other ways to initialize a {@code MockMultipartHttpServletRequest}, 070 * see {@link #with(RequestPostProcessor)} and the 071 * {@link RequestPostProcessor} extension point. 072 * @param uri the URL 073 * @since 4.0.3 074 */ 075 MockMultipartHttpServletRequestBuilder(URI uri) { 076 super(HttpMethod.POST, uri); 077 super.contentType(MediaType.MULTIPART_FORM_DATA); 078 } 079 080 081 /** 082 * Create a new MockMultipartFile with the given content. 083 * @param name the name of the file 084 * @param content the content of the file 085 */ 086 public MockMultipartHttpServletRequestBuilder file(String name, byte[] content) { 087 this.files.add(new MockMultipartFile(name, content)); 088 return this; 089 } 090 091 /** 092 * Add the given MockMultipartFile. 093 * @param file the multipart file 094 */ 095 public MockMultipartHttpServletRequestBuilder file(MockMultipartFile file) { 096 this.files.add(file); 097 return this; 098 } 099 100 /** 101 * Add {@link Part} components to the request. 102 * @param parts one or more parts to add 103 * @since 5.0 104 */ 105 public MockMultipartHttpServletRequestBuilder part(Part... parts) { 106 Assert.notEmpty(parts, "'parts' must not be empty"); 107 for (Part part : parts) { 108 this.parts.add(part.getName(), part); 109 } 110 return this; 111 } 112 113 @Override 114 public Object merge(@Nullable Object parent) { 115 if (parent == null) { 116 return this; 117 } 118 if (parent instanceof MockHttpServletRequestBuilder) { 119 super.merge(parent); 120 if (parent instanceof MockMultipartHttpServletRequestBuilder) { 121 MockMultipartHttpServletRequestBuilder parentBuilder = (MockMultipartHttpServletRequestBuilder) parent; 122 this.files.addAll(parentBuilder.files); 123 parentBuilder.parts.keySet().stream().forEach(name -> 124 this.parts.putIfAbsent(name, parentBuilder.parts.get(name))); 125 } 126 127 } 128 else { 129 throw new IllegalArgumentException("Cannot merge with [" + parent.getClass().getName() + "]"); 130 } 131 return this; 132 } 133 134 /** 135 * Create a new {@link MockMultipartHttpServletRequest} based on the 136 * supplied {@code ServletContext} and the {@code MockMultipartFiles} 137 * added to this builder. 138 */ 139 @Override 140 protected final MockHttpServletRequest createServletRequest(ServletContext servletContext) { 141 142 MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(servletContext); 143 this.files.stream().forEach(request::addFile); 144 this.parts.values().stream().flatMap(Collection::stream).forEach(request::addPart); 145 146 if (!this.parts.isEmpty()) { 147 new StandardMultipartHttpServletRequest(request) 148 .getMultiFileMap().values().stream().flatMap(Collection::stream) 149 .forEach(request::addFile); 150 } 151 152 return request; 153 } 154 155}