From 0cc3abbcafbfebdd602e19b48b04773171418c40 Mon Sep 17 00:00:00 2001 From: wangyu <727842003@qq.com> Date: Mon, 30 Jan 2023 13:02:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201.0.4=E7=89=88=E6=9C=AC=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=EF=BC=8C=E6=94=AF=E6=8C=81=E6=B3=A8=E8=A7=A3=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flyfish/rest/annotation/RestPart.java | 37 +++++++++ .../RestClientConfiguration.java | 7 +- .../rest/core/client/RestClientBuilder.java | 6 +- .../support/AbstractBodyResolver.java | 22 ++++-- .../rest/registry/proxy/RestProxyInvoker.java | 8 ++ .../proxy/support/ArgumentResolveContext.java | 63 ++++++++++++++- .../resolvers/RestBodyArgumentResolver.java | 2 +- .../resolvers/RestHeaderArgumentResolver.java | 2 +- .../resolvers/RestPartArgumentResolver.java | 76 +++++++++++++++++++ .../RestPathParamArgumentResolver.java | 2 +- .../group/flyfish/rest/utils/DataUtils.java | 10 +++ .../group/flyfish/rest/utils/JacksonUtil.java | 6 ++ .../group/flyfish/rest/MultipartTest.java | 60 +++++++++++++++ 13 files changed, 286 insertions(+), 15 deletions(-) create mode 100644 rest-proxy-api/src/main/java/group/flyfish/rest/annotation/RestPart.java create mode 100644 rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPartArgumentResolver.java create mode 100644 rest-proxy-core/src/test/java/group/flyfish/rest/MultipartTest.java diff --git a/rest-proxy-api/src/main/java/group/flyfish/rest/annotation/RestPart.java b/rest-proxy-api/src/main/java/group/flyfish/rest/annotation/RestPart.java new file mode 100644 index 0000000..e0892b5 --- /dev/null +++ b/rest-proxy-api/src/main/java/group/flyfish/rest/annotation/RestPart.java @@ -0,0 +1,37 @@ +package group.flyfish.rest.annotation; + +import java.lang.annotation.*; + +/** + * rest请求体标记 + * + * @author wangyu + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RestPart { + + /** + * 请求部分名称 + * + * @return 传入multipart中key + */ + String value() default "file"; + + /** + * 绑定文件名称 + */ + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @interface Filename { + + /** + * 请求部分名称 + * + * @return 传入multipart中key + */ + String value() default "file"; + } +} diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java b/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java index ceec975..c7bd60f 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/configuration/RestClientConfiguration.java @@ -7,10 +7,7 @@ import group.flyfish.rest.mapping.RestResultMapping; import group.flyfish.rest.registry.RestApiRegistry; import group.flyfish.rest.registry.proxy.support.RestArgumentResolver; import group.flyfish.rest.registry.proxy.support.RestArgumentResolverComposite; -import group.flyfish.rest.registry.proxy.support.resolvers.RestBodyArgumentResolver; -import group.flyfish.rest.registry.proxy.support.resolvers.RestHeaderArgumentResolver; -import group.flyfish.rest.registry.proxy.support.resolvers.RestParamArgumentResolver; -import group.flyfish.rest.registry.proxy.support.resolvers.RestPathParamArgumentResolver; +import group.flyfish.rest.registry.proxy.support.resolvers.*; import group.flyfish.rest.utils.DataUtils; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -85,6 +82,8 @@ public class RestClientConfiguration { public RestArgumentResolverComposite restArgumentResolverComposite() { List resolvers = Arrays.asList( new RestPathParamArgumentResolver(), + new RestPartArgumentResolver(), + new RestPartArgumentResolver.FilenameResolver(), new RestBodyArgumentResolver(), new RestHeaderArgumentResolver(), new RestParamArgumentResolver() diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/client/RestClientBuilder.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/client/RestClientBuilder.java index 890c9ac..028540e 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/core/client/RestClientBuilder.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/client/RestClientBuilder.java @@ -121,10 +121,14 @@ public class RestClientBuilder { } public RestClientBuilder addMultipartBody(String name, String filename, Object data) { + return addMultipartBody(new Multipart(name, filename, data)); + } + + public RestClientBuilder addMultipartBody(Multipart part) { if (null == this.multipartList) { this.multipartList = new ArrayList<>(); } - this.multipartList.add(new Multipart(name, filename, data)); + this.multipartList.add(part); return this; } diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java index 71b1ec5..55e4ecf 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/core/resolver/support/AbstractBodyResolver.java @@ -12,6 +12,7 @@ import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.message.BasicNameValuePair; import java.io.File; +import java.io.InputStream; import java.nio.charset.Charset; import java.util.List; import java.util.Map; @@ -52,13 +53,11 @@ public abstract class AbstractBodyResolver { String name = multipart.getName(); String filename = multipart.getFilename(); if (data instanceof byte[]) { - builder.addBinaryBody(name, (byte[]) data, - ContentType.create(MIME_MAP.getOrDefault(DataUtils.getExtension(filename), - ContentType.APPLICATION_OCTET_STREAM.getMimeType())), filename); + builder.addBinaryBody(name, (byte[]) data, resolveType(filename), filename); } else if (data instanceof File) { - builder.addBinaryBody(name, (File) data, - ContentType.create(MIME_MAP.getOrDefault(DataUtils.getExtension(filename), - ContentType.APPLICATION_OCTET_STREAM.getMimeType())), filename); + builder.addBinaryBody(name, (File) data, resolveType(filename), filename); + } else if (data instanceof InputStream) { + builder.addBinaryBody(name, (InputStream) data, resolveType(filename), filename); } else { throw new IllegalArgumentException("上传时,输入的数据不被支持!"); } @@ -100,4 +99,15 @@ public abstract class AbstractBodyResolver { } return null; } + + /** + * 解析内容类型 + * + * @param filename 文件名 + * @return 结果 + */ + private ContentType resolveType(String filename) { + return ContentType.create(MIME_MAP.getOrDefault(DataUtils.getExtension(filename), + ContentType.APPLICATION_OCTET_STREAM.getMimeType())); + } } diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java index 3b63494..4001e29 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/RestProxyInvoker.java @@ -139,6 +139,14 @@ public class RestProxyInvoker implements InvocationHandler, PropertiesConfigu if (context.hasBody()) { builder.body(context.getBody()); } + // 赋值文件体 + if (context.hasMultipart()) { + builder.multipart(); + context.getFiles().forEach((key, value) -> { + value.setFilename(context.getFilename(key, value.getFilename())); + builder.addMultipartBody(value); + }); + } // 赋值参数们 if (context.hasParams()) { builder.queryParams(context.getParam()); diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java index f88d4e6..c856955 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/ArgumentResolveContext.java @@ -1,6 +1,7 @@ package group.flyfish.rest.registry.proxy.support; import group.flyfish.rest.annotation.RestApi; +import group.flyfish.rest.core.entity.Multipart; import group.flyfish.rest.utils.DataUtils; import lombok.Builder; import lombok.Data; @@ -32,6 +33,12 @@ public class ArgumentResolveContext { // 请求体 private Object body; + // 文件列表 + private Map files; + + // 文件名列表 + private Map filenames; + // 设置参数 public void setParam(String key, Object value) { if (DataUtils.isEmpty(param)) { @@ -74,7 +81,12 @@ public class ArgumentResolveContext { this.headers.put(name, value); } - // 设置路径参数 + /** + * 设置路径参数 + * + * @param key 名称 + * @param value 值 + */ public void setPathParam(String key, Object value) { if (DataUtils.isEmpty(pathParams)) { pathParams = new HashMap<>(); @@ -82,6 +94,44 @@ public class ArgumentResolveContext { pathParams.put(key, value); } + /** + * 设置文件 + * + * @param name 文件key + * @param filename 文件名 + * @param file 文件数据 + */ + public void setMultipart(String name, String filename, Object file) { + setMultipart(new Multipart(name, filename, file)); + } + + /** + * 添加文件名 + * + * @param part 文件部分key + * @param filename 文件名 + */ + public void addFilename(String part, String filename) { + if (DataUtils.isEmpty(filenames)) { + filenames = new HashMap<>(); + } + filenames.put(part, filename); + } + + /** + * 设置文件 + * + * @param multipart 文件 + */ + public void setMultipart(Multipart multipart) { + if (DataUtils.isEmpty(files)) { + files = new HashMap<>(); + } + if (null != multipart && null != multipart.getData()) { + files.put(multipart.getName(), multipart); + } + } + public boolean hasPathParams() { return DataUtils.isNotEmpty(pathParams); } @@ -97,4 +147,15 @@ public class ArgumentResolveContext { public boolean hasParams() { return DataUtils.isNotEmpty(param); } + + public boolean hasMultipart() { + return DataUtils.isNotEmpty(files); + } + + public String getFilename(String part, String initial) { + if (null == filenames || !filenames.containsKey(part)) { + return initial; + } + return filenames.get(part); + } } diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java index 4bd20df..6079488 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestBodyArgumentResolver.java @@ -21,7 +21,7 @@ public class RestBodyArgumentResolver implements RestArgumentResolver { */ @Override public boolean support(Parameter parameter) { - return null != parameter.getAnnotation(RestBody.class); + return parameter.isAnnotationPresent(RestBody.class); } /** diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java index 38ddd6a..a4cec0b 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestHeaderArgumentResolver.java @@ -23,7 +23,7 @@ public class RestHeaderArgumentResolver implements RestArgumentResolver { */ @Override public boolean support(Parameter parameter) { - return null != parameter.getAnnotation(RestHeader.class); + return parameter.isAnnotationPresent(RestHeader.class); } /** diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPartArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPartArgumentResolver.java new file mode 100644 index 0000000..d29201e --- /dev/null +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPartArgumentResolver.java @@ -0,0 +1,76 @@ +package group.flyfish.rest.registry.proxy.support.resolvers; + +import group.flyfish.rest.annotation.RestPart; +import group.flyfish.rest.core.entity.Multipart; +import group.flyfish.rest.registry.proxy.support.ArgumentResolveContext; +import group.flyfish.rest.registry.proxy.support.RestArgumentResolver; + +import java.lang.reflect.Parameter; + +/** + * 文件上传参数处理器 + * + * @author wangyu + */ +public class RestPartArgumentResolver implements RestArgumentResolver { + + /** + * 是否支持 + * + * @param parameter 参数 + * @return 结果 + */ + @Override + public boolean support(Parameter parameter) { + return parameter.isAnnotationPresent(RestPart.class); + } + + /** + * 解析 + * + * @param context 上下文,赋值 + * @param parameter 参数 + * @param value 值 + */ + @Override + public void resolve(ArgumentResolveContext context, Parameter parameter, Object value) { + RestPart part = parameter.getAnnotation(RestPart.class); + if (value instanceof Multipart) { + context.setMultipart((Multipart) value); + } else if (null != value) { + context.setMultipart(part.value(), null, value); + } + } + + /** + * 解析和处理文件名 + */ + public static class FilenameResolver implements RestArgumentResolver { + + /** + * 是否支持 + * + * @param parameter 参数 + * @return 结果 + */ + @Override + public boolean support(Parameter parameter) { + return parameter.isAnnotationPresent(RestPart.Filename.class); + } + + /** + * 解析 + * + * @param context 上下文,赋值 + * @param parameter 参数 + * @param value 值 + */ + @Override + public void resolve(ArgumentResolveContext context, Parameter parameter, Object value) { + if (value instanceof String) { + RestPart.Filename filename = parameter.getAnnotation(RestPart.Filename.class); + context.addFilename(filename.value(), (String) value); + } + } + } +} diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java index 945a8a0..f648add 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/registry/proxy/support/resolvers/RestPathParamArgumentResolver.java @@ -22,7 +22,7 @@ public class RestPathParamArgumentResolver implements RestArgumentResolver { */ @Override public boolean support(Parameter parameter) { - return null != parameter.getAnnotation(RestPathParam.class); + return parameter.isAnnotationPresent(RestPathParam.class); } /** diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java index 0726d93..64ad168 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/DataUtils.java @@ -80,6 +80,16 @@ public final class DataUtils { return CollectionUtils.isEmpty(map); } + /** + * 判断集合是否为空 + * + * @param collection 集合 + * @return 结果 + */ + public static boolean isEmpty(Collection collection) { + return CollectionUtils.isEmpty(collection); + } + /** * 是否为空 * diff --git a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java index 5e8c1e1..3fe00e8 100644 --- a/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java +++ b/rest-proxy-core/src/main/java/group/flyfish/rest/utils/JacksonUtil.java @@ -77,6 +77,9 @@ public final class JacksonUtil { if (null == json || "".equals(json)) { return null; } + if (String.class.isAssignableFrom(clazz)) { + return DataUtils.cast(json); + } try { return mapper.readValue(json, clazz); } catch (IOException e) { @@ -86,6 +89,9 @@ public final class JacksonUtil { } public static T fromJson(final String json, JavaType type) { + if (type.isTypeOrSubTypeOf(String.class)) { + return DataUtils.cast(json); + } if (null == json || "".equals(json)) { return null; } diff --git a/rest-proxy-core/src/test/java/group/flyfish/rest/MultipartTest.java b/rest-proxy-core/src/test/java/group/flyfish/rest/MultipartTest.java new file mode 100644 index 0000000..432c681 --- /dev/null +++ b/rest-proxy-core/src/test/java/group/flyfish/rest/MultipartTest.java @@ -0,0 +1,60 @@ +package group.flyfish.rest; + +import group.flyfish.rest.annotation.RestApi; +import group.flyfish.rest.annotation.RestPart; +import group.flyfish.rest.annotation.RestService; +import group.flyfish.rest.container.RestTestContainer; +import group.flyfish.rest.core.entity.Multipart; +import group.flyfish.rest.enums.HttpMethod; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.stereotype.Component; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +@RunWith(SpringRunner.class) +@WebAppConfiguration +@SpringBootTest(classes = RestTestContainer.class) +@ComponentScan("sys.test") +@Slf4j +@Component +public class MultipartTest { + + @Resource + private TestRestService testRestService; + + /** + * 测试入口 + */ + @Test + public void test() { + File file = new File("/Users/wangyu/Desktop/2022年终述职报告.docx"); + String filename = testRestService.uploadPart(new Multipart("file", file.getName(), file)); + System.out.println(filename); + try (InputStream in = Files.newInputStream(file.toPath())) { + filename = testRestService.uploadAnno(in, file.getName()); + System.out.println(filename); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @RestService(baseUrl = "http://localhost:8999", timeout = 500) + public interface TestRestService { + + @RestApi(uri = "/files", method = HttpMethod.POST) + String uploadPart(@RestPart Multipart file); + + @RestApi(uri = "/files", method = HttpMethod.POST) + String uploadAnno(@RestPart("fbl") InputStream file, @RestPart.Filename("fbl") String name); + } +}