Skip to content

Commit

Permalink
[SCB-2058] fix SpringMVC provider not support non-file-type RequestPa…
Browse files Browse the repository at this point in the history
…rt problem

- add MultipartFilePropertyCreator to enable ModelConverters resolving MultipartFile param
- make RequestPartAnnotationProcessor recognize non-file-type RequestPart param
  • Loading branch information
yhs0092 committed Aug 3, 2020
1 parent 70756fe commit cad862e
Show file tree
Hide file tree
Showing 8 changed files with 423 additions and 5 deletions.
Expand Up @@ -20,19 +20,25 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.servicecomb.it.Consumers;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

public class TestUpload {

Expand All @@ -47,6 +53,10 @@ public class TestUpload {
private static final String message = "cseMessage";

interface UploadIntf {
Map<String, String> uploadMultiformMix(Resource file,
List<Resource> fileList,
String str,
List<String> strList);
}

private static Consumers<UploadIntf> consumersSpringmvc = new Consumers<>("uploadSpringmvcSchema",
Expand Down Expand Up @@ -316,6 +326,78 @@ public void testFileUploadMixWithoutAnnotation() {
Assert.assertTrue(containsAll(result, "hello1", "cse4", "cse3", "中文 2", message));
}

@Test
public void testUploadMultiformMix_RestTemplate_SpringMVC() {
Map<String, Object> map = new HashMap<>();
List<Resource> fileList = new ArrayList<>();
fileList.add(fileSystemResource2);
map.put("file", fileSystemResource1);
map.put("fileList", fileList);
map.put("str", message);
map.put("strList", Collections.singletonList("2.中文测试"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
ResponseEntity<Map<String, String>> response =
consumersSpringmvc.getSCBRestTemplate().exchange("/uploadMultiformMix", HttpMethod.POST,
new HttpEntity<>(map, headers), new ParameterizedTypeReference<Map<String, String>>() {
});
Map<String, String> responseBody = response.getBody();
Assert.assertThat(responseBody, Matchers.notNullValue());
Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
}

@Test
public void testUploadMultiformMix_Rpc_SpringMVC() {
List<Resource> fileList = new ArrayList<>();
fileList.add(fileSystemResource2);
Map<String, String> responseBody =
consumersSpringmvc.getIntf().uploadMultiformMix(
fileSystemResource1, fileList, message, Collections.singletonList("2.中文测试"));
Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
}

@Test
public void testUploadMultiformMix_RestTemplate_JAXRS() {
Map<String, Object> map = new HashMap<>();
List<FileSystemResource> fileList = new ArrayList<>();
fileList.add(fileSystemResource2);
map.put("file", fileSystemResource1);
map.put("fileList", fileList);
map.put("str", message);
map.put("strList", Collections.singletonList("2.中文测试"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
ResponseEntity<Map<String, String>> response =
consumersJaxrs.getSCBRestTemplate().exchange("/uploadMultiformMix", HttpMethod.POST,
new HttpEntity<>(map, headers), new ParameterizedTypeReference<Map<String, String>>() {
});
Map<String, String> responseBody = response.getBody();
Assert.assertThat(responseBody, Matchers.notNullValue());
Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
}

@Test
public void testUploadMultiformMix_Rpc_JAXRS() {
List<Resource> fileList = new ArrayList<>();
fileList.add(fileSystemResource2);
Map<String, String> responseBody =
consumersJaxrs.getIntf().uploadMultiformMix(
fileSystemResource1, fileList, message, Collections.singletonList("2.中文测试"));
Assert.assertThat(responseBody.get("file"), Matchers.is("hello1"));
Assert.assertThat(responseBody.get("fileList"), Matchers.is("中文 2"));
Assert.assertThat(responseBody.get("str"), Matchers.is("cseMessage"));
Assert.assertThat(responseBody.get("strList"), Matchers.is("[2.中文测试]"));
}

private static boolean containsAll(String str, String... strings) {
for (String string : strings) {
if (!str.contains(string)) {
Expand Down
Expand Up @@ -18,8 +18,11 @@

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Part;
import javax.ws.rs.FormParam;
Expand All @@ -30,6 +33,7 @@

import org.apache.commons.io.IOUtils;
import org.apache.servicecomb.provider.rest.common.RestSchema;
import org.springframework.web.multipart.MultipartFile;

@RestSchema(schemaId = "uploadJaxrsSchema")
@Path("/v1/uploadJaxrsSchema")
Expand Down Expand Up @@ -138,4 +142,30 @@ private static String getStrFromPart(Part file1) {
}
return "";
}

@Path("/uploadMultiformMix")
@POST
public Map<String, String> uploadMultiformMix(@FormParam("file") MultipartFile file,
@FormParam("fileList") List<MultipartFile> fileList,
@FormParam("str") String str,
@FormParam("strList") List<String> strList) throws IOException {
HashMap<String, String> map = new HashMap<>();
map.put("file", new String(file.getBytes(), StandardCharsets.UTF_8.name()));
map.put("fileList", _fileUpload(fileList));
map.put("str", str);
map.put("strList", strList.toString());
return map;
}

private static String _fileUpload(List<MultipartFile> fileList) {
StringBuilder stringBuilder = new StringBuilder();
try {
for (MultipartFile multipartFile : fileList) {
stringBuilder.append(IOUtils.toString(multipartFile.getBytes(), StandardCharsets.UTF_8.name()));
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return stringBuilder.toString();
}
}
Expand Up @@ -20,7 +20,9 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.servicecomb.provider.rest.common.RestSchema;
Expand Down Expand Up @@ -96,6 +98,19 @@ public String uploadMix(@RequestPart(name = "file1") List<MultipartFile> file1,
return _fileUpload(file1) + name;
}

@RequestMapping(path = "/uploadMultiformMix", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Map<String, String> uploadMultiformMix(@RequestPart(name = "file") MultipartFile file,
@RequestPart(name = "fileList") List<MultipartFile> fileList,
@RequestPart("str") String str,
@RequestPart("strList") List<String> strList) throws IOException {
HashMap<String, String> map = new HashMap<>();
map.put("file", new String(file.getBytes(), StandardCharsets.UTF_8.name()));
map.put("fileList", _fileUpload(fileList));
map.put("str", str);
map.put("strList", strList.toString());
return map;
}

private static String _fileUpload(List<MultipartFile> fileList) {
StringBuilder stringBuilder = new StringBuilder();
try {
Expand Down
Expand Up @@ -23,13 +23,14 @@
import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType;
import org.springframework.web.bind.annotation.RequestPart;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;

import io.swagger.converter.ModelConverters;
import io.swagger.models.Operation;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.FormParameter;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.FileProperty;
import io.swagger.models.properties.Property;

public class RequestPartAnnotationProcessor implements
Expand All @@ -56,11 +57,34 @@ public HttpParameterType getHttpParameterType(RequestPart parameterAnnotation) {
@Override
public void fillParameter(Swagger swagger, Operation operation, FormParameter formParameter, Type type,
RequestPart requestPart) {
Property property = new FileProperty();
if (TypeFactory.defaultInstance().constructType(type).isContainerType()) {
property = new ArrayProperty(new FileProperty());
}
Property property = resolveParamProperty(type);

formParameter.setProperty(property);
formParameter.setRequired(requestPart.required());
}

private Property resolveParamProperty(Type type) {
JavaType javaType = TypeFactory.defaultInstance().constructType(type);
if (javaType.isContainerType()) {
return resolvePropertyAsContainerType(javaType);
}
return ModelConverters.getInstance().readAsProperty(type);
}

private Property resolvePropertyAsContainerType(JavaType javaType) {
// At present, only array and collection of Part params are supported,
// but Map type is also a kind of container type.
// Although Map is not supported now, we still consider to take the type of value to generate a property.
// Therefore, here we use lastContainedTypeIndex to get the contained type.
int lastContainedTypeIndex = javaType.containedTypeCount() - 1;
JavaType containedItemType;
if (lastContainedTypeIndex < 0) {
// javaType may be an array
containedItemType = javaType.getContentType();
} else {
containedItemType = javaType.containedType(lastContainedTypeIndex);
}
Property containedItemProperty = ModelConverters.getInstance().readAsProperty(containedItemType);
return new ArrayProperty(containedItemProperty);
}
}
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* http://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.apache.servicecomb.swagger.generator.springmvc.property.creator;

import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator;
import org.springframework.web.multipart.MultipartFile;

import io.swagger.models.properties.FileProperty;
import io.swagger.models.properties.Property;

public class MultipartFilePropertyCreator implements PropertyCreator {
private final Class<?>[] classes = {MultipartFile.class};

@Override
public Property createProperty() {
return new FileProperty();
}

@Override
public Class<?>[] classes() {
return classes;
}
}
@@ -0,0 +1,18 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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
#
# http://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.
#

org.apache.servicecomb.swagger.generator.springmvc.property.creator.MultipartFilePropertyCreator

0 comments on commit cad862e

Please sign in to comment.