diff --git a/doc/docs/files/90100_常见问题.md b/doc/docs/files/90100_常见问题.md index e2b4601f..b1177e01 100644 --- a/doc/docs/files/90100_常见问题.md +++ b/doc/docs/files/90100_常见问题.md @@ -67,4 +67,20 @@ public static class Swagger2 extends SwaggerSupport { return false; } } +``` + +## 调试网关出现服务不可用 + +打断点调试,网关出现Read Timeout + +参考:https://blog.csdn.net/qq_36872046/article/details/81058045 + +yml添加: + +```yaml +ribbon: + # https://blog.csdn.net/qq_36872046/article/details/81058045 + # 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。 + # 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点 + ReadTimeout: 60000 ``` \ No newline at end of file diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java index 01517ecf..a1748613 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java @@ -14,7 +14,7 @@ import java.util.List; */ public class AlipayServiceConfiguration extends BaseServiceConfiguration { - public AlipayServiceConfiguration() { + static { // 默认版本号为1.0 ServiceConfig.getInstance().setDefaultVersion("1.0"); } diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java index c39ba27c..dd9a5f12 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java @@ -28,7 +28,11 @@ public class OpenServiceConfig extends AlipayServiceConfiguration { .addResourceLocations("classpath:/META-INF/resources/webjars/"); } - // 开启文档 + + /** + * 开启文档 + * http://ip:port/v2/api-docs + */ @Configuration @EnableSwagger2 public static class Swagger2 extends SwaggerSupport { diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java index bcce526b..13b38327 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java @@ -5,6 +5,10 @@ import com.gitee.sop.servercommon.util.UploadUtil; import com.gitee.sop.storyweb.controller.param.FileUploadParam; import com.gitee.sop.storyweb.controller.param.FileUploadParam2; import com.gitee.sop.storyweb.vo.FileUploadVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; import org.apache.commons.io.IOUtils; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -19,6 +23,7 @@ import java.util.Collection; * @author tanghc */ @RestController +@Api(tags = "文件上传") public class FileUploadDemoController { /** @@ -26,6 +31,7 @@ public class FileUploadDemoController { * @param param * @return */ + @ApiOperation(value = "文件上传例1", notes = "上传文件demo") @ApiMapping(value = "demo.file.upload") public FileUploadVO file1(FileUploadParam param) { System.out.println(param.getRemark()); @@ -48,6 +54,9 @@ public class FileUploadDemoController { * @param param * @return */ + @ApiOperation(value = "文件上传例2", notes = "可上传多个文件" + // 多文件上传、不确定文件数量上传,必须申明下面这句,否则沙盒界面不会出现上传控件 + , extensions = @Extension(properties = @ExtensionProperty(name = "multiple", value = "multiple"))) @ApiMapping(value = "demo.file.upload2") public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) { System.out.println(param.getRemark()); diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/param/FileUploadParam.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/param/FileUploadParam.java index ac778bf2..2be7875c 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/param/FileUploadParam.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/param/FileUploadParam.java @@ -1,5 +1,6 @@ package com.gitee.sop.storyweb.controller.param; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.web.multipart.MultipartFile; @@ -14,8 +15,10 @@ public class FileUploadParam { // 上传文件,字段名称对应表单中的name属性值 @NotNull(message = "请上传文件1") + @ApiModelProperty(value = "文件1", required = true) private MultipartFile file1; @NotNull(message = "请上传文件2") + @ApiModelProperty(value = "文件2", required = true) private MultipartFile file2; } diff --git a/sop-gateway/src/main/resources/application-dev.yml b/sop-gateway/src/main/resources/application-dev.yml index 568556ce..49b49adc 100644 --- a/sop-gateway/src/main/resources/application-dev.yml +++ b/sop-gateway/src/main/resources/application-dev.yml @@ -15,6 +15,10 @@ zuul: secret: MZZOUSTua6LzApIWXCwEgbBmxSzpzC ribbon: + # https://blog.csdn.net/qq_36872046/article/details/81058045 + # 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。 + # 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点 + ReadTimeout: 2000 # 设置为true(默认false),则所有请求都重试,默认只支持get请求重试 # 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性 OkToRetryOnAllOperations: false diff --git a/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/common/OpenConfig.java b/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/common/OpenConfig.java index 4d47c629..e8b64015 100644 --- a/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/common/OpenConfig.java +++ b/sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/common/OpenConfig.java @@ -50,11 +50,11 @@ public class OpenConfig { private String errorResponseName = "error_response"; /** 请求超时时间 */ - private int connectTimeoutSeconds = 10; + private int connectTimeoutSeconds = 60; /** http读取超时时间 */ - private int readTimeoutSeconds = 10; + private int readTimeoutSeconds = 60; /** http写超时时间 */ - private int writeTimeoutSeconds = 10; + private int writeTimeoutSeconds = 60; /** * 构建数据节点名称 diff --git a/sop-website/website-front/pages/sandbox/sandbox.html b/sop-website/website-front/pages/sandbox/sandbox.html index 5428c532..1dd4b6cd 100644 --- a/sop-website/website-front/pages/sandbox/sandbox.html +++ b/sop-website/website-front/pages/sandbox/sandbox.html @@ -104,6 +104,9 @@
+
@@ -136,7 +139,7 @@ - + \ No newline at end of file diff --git a/sop-website/website-front/pages/sandbox/sandbox.js b/sop-website/website-front/pages/sandbox/sandbox.js index 1baec6cb..b338de7c 100644 --- a/sop-website/website-front/pages/sandbox/sandbox.js +++ b/sop-website/website-front/pages/sandbox/sandbox.js @@ -20,6 +20,7 @@ function selectItem(docItem, layui) { currentItem = docItem; resetResultDiv(); var nameVersion = docItem.nameVersion; + var multiple = docItem.multiple; treetable = treetable || layui.treetable; $('.sop-name').text(docItem.name); $('.sop-version').text(docItem.version); @@ -28,6 +29,8 @@ function selectItem(docItem, layui) { createRequestParameter(docItem); + $('#multipleDiv').css('display', multiple ? 'block' : 'none'); + var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]'); $li.addClass('layui-this').siblings().removeClass('layui-this'); InputCache.init(); @@ -77,11 +80,12 @@ function createTreeTable(id, data) { var id = currentItem.nameVersion + '-' + row.name; var requiredTxt = row.required ? 'required lay-verify="required"' : ''; var module = row.module; + var type = row.type == 'file' ? 'file' : 'text'; var attrs = [ 'id="' + id + '"' , 'name="'+row.name+'"' , 'class="layui-input test-input"' - , 'type="text"' + , 'type="' + type + '"' , requiredTxt , 'module="'+module+'"' ]; @@ -102,6 +106,7 @@ function doTest() { method: method, version: version }; + var uploadFileObjects = getUploadFileObjects(); var $inputs = $body.find('.test-input'); var bizContent = {}; $inputs.each(function () { @@ -111,32 +116,92 @@ function doTest() { bizContent[module] = {}; } var moduleObj = bizContent[module]; - moduleObj[this.name] = this.value; + putVal(moduleObj, this); } else { - bizContent[this.name] = this.value; + putVal(bizContent, this); } }); data.bizContent = JSON.stringify(bizContent); + // 确定文件数量,并且知道参数名称 + if (uploadFileObjects.length > 0) { + var formData = new FormData(); + for (var i = 0; i < uploadFileObjects.length; i++) { + var fileInput = uploadFileObjects[i]; + formData.append(fileInput.name, fileInput.file); + } + postFile(data, formData); + } else { + postJson(data); + } +} + +function putVal(obj, input) { + if (input.type == 'text') { + obj[input.name] = input.value; + } +} + +function getUploadFileObjects() { + var fileObjects = []; + $body.find('input[type=file]') + .each(function () { + var $input = $(this); + var name = this.name; + var fileList = $input.prop('files'); + var multiple = $input.prop('multiple'); + if (multiple) { + for (var i = 0; i < fileList.length; i++) { + fileObjects.push({name: name + i, file: fileList[i]}); + } + } else { + fileObjects.push({name: name, file: fileList[0]}); + } + }); + return fileObjects; +} + +function postJson(data) { $.ajax({ url: SopConfig.url + '/sandbox/test' , dataType: 'json' , data: data , method: 'post' - , success: function (resp) { - setReqInfo(resp); - showRespnfo(resp.apiResult); - } - , error: function (xhr,status,error) { - // {"timestamp":"2019-06-19 15:57:36","status":500,"error":"Internal Server Error","message":"appId不能为空","path":"/sandbox/test"} - var errorData = xhr.responseJSON; - if (errorData) { - setReqInfo(''); - showRespnfo(errorData.message); - } - } + , success: successHandler + , error: errorHandler }); } +function postFile(data, formData) { + for(var key in data) { + formData.append(key, data[key]); + } + $.ajax({ + url: SopConfig.url + '/sandbox/test' + , data: formData + // ajax2.0可以不用设置请求头,但是jq帮我们自动设置了,这样的话需要我们自己取消掉 + , contentType: false + // 取消帮我们格式化数据,是什么就是什么 + , processData: false + , method: 'post' + , success: successHandler + , error: errorHandler + }); +} + +function successHandler(resp) { + setReqInfo(resp); + showRespnfo(resp.apiResult); +} + +function errorHandler(xhr,status,error) { + // {"timestamp":"2019-06-19 15:57:36","status":500,"error":"Internal Server Error","message":"appId不能为空","path":"/sandbox/test"} + var errorData = xhr.responseJSON; + if (errorData) { + setReqInfo(''); + showRespnfo(errorData.message); + } +} + function showRespnfo(info) { var json = formatJson(info); setRespInfo(json); diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java index 431c9482..2d933f9c 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java @@ -14,6 +14,8 @@ public class DocItem { private String version; private String summary; private String description; + // 是否多文件上传 + private boolean multiple; List requestParameters; List responseParameters; diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java new file mode 100644 index 00000000..b9a6c727 --- /dev/null +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java @@ -0,0 +1,189 @@ +package com.gitee.sop.websiteserver.bean; + +import lombok.Data; +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.FormBody; +import okhttp3.HttpUrl; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * @author tanghc + */ +public class HttpTool { + private static final String METHOD_GET = "get"; + private Map> cookieStore = new HashMap>(); + + private OkHttpClient httpClient; + + public HttpTool() { + this(new HttpToolConfig()); + } + + public HttpTool(HttpToolConfig httpToolConfig) { + this.initHttpClient(httpToolConfig); + } + + protected void initHttpClient(HttpToolConfig httpToolConfig) { + httpClient = new OkHttpClient.Builder() + .connectTimeout(httpToolConfig.connectTimeoutSeconds, TimeUnit.SECONDS) // 设置链接超时时间,默认10秒 + .readTimeout(httpToolConfig.readTimeoutSeconds, TimeUnit.SECONDS) + .writeTimeout(httpToolConfig.writeTimeoutSeconds, TimeUnit.SECONDS) + .cookieJar(new CookieJar() { + public void saveFromResponse(HttpUrl httpUrl, List list) { + cookieStore.put(httpUrl.host(), list); + } + public List loadForRequest(HttpUrl httpUrl) { + List cookies = cookieStore.get(httpUrl.host()); + return cookies != null ? cookies : new ArrayList(); + } + }).build(); + } + + @Data + public static class HttpToolConfig { + /** 请求超时时间 */ + private int connectTimeoutSeconds = 10; + /** http读取超时时间 */ + private int readTimeoutSeconds = 10; + /** http写超时时间 */ + private int writeTimeoutSeconds = 10; + } + + /** + * get请求 + * + * @param url + * @param header + * @return + * @throws IOException + */ + public String get(String url, Map header) throws IOException { + Request.Builder builder = new Request.Builder().url(url).get(); + // 添加header + addHeader(builder, header); + + Request request = builder.build(); + Response response = httpClient.newCall(request).execute(); + return response.body().string(); + } + + /** + * 提交表单 + * + * @param url url + * @param form 参数 + * @param header header + * @param method 请求方式,post,get等 + * @return + * @throws IOException + */ + public String request(String url, Map form, Map header, String method) throws IOException { + Request.Builder requestBuilder; + if (METHOD_GET.equalsIgnoreCase(method)) { + HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); + for (Map.Entry entry : form.entrySet()) { + urlBuilder.addQueryParameter(entry.getKey(), entry.getValue()); + } + requestBuilder = new Request.Builder() + .url(urlBuilder.build()) + .get(); + } else { + FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8); + for (Map.Entry entry : form.entrySet()) { + paramBuilder.add(entry.getKey(), entry.getValue()); + } + FormBody formBody = paramBuilder.build(); + requestBuilder = new Request.Builder() + .url(url) + .method(method, formBody); + } + // 添加header + addHeader(requestBuilder, header); + + Request request = requestBuilder.build(); + Response response = httpClient + .newCall(request) + .execute(); + try { + return response.body().string(); + } finally { + response.close(); + } + } + + /** + * 提交表单,并且上传文件 + * + * @param url + * @param form + * @param header + * @param files + * @return + * @throws IOException + */ + public String requestFile(String url, Map form, Map header, List files) + throws IOException { + // 创建MultipartBody.Builder,用于添加请求的数据 + MultipartBody.Builder bodyBuilder = new MultipartBody.Builder(); + bodyBuilder.setType(MultipartBody.FORM); + + for (UploadFile uploadFile : files) { + bodyBuilder.addFormDataPart(uploadFile.getName(), // 请求的名字 + uploadFile.getFileName(), // 文件的文字,服务器端用来解析的 + RequestBody.create(null, uploadFile.getFileData()) // 创建RequestBody,把上传的文件放入 + ); + } + + Set> entrySet = form.entrySet(); + for (Map.Entry entry : entrySet) { + bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue()); + } + + RequestBody requestBody = bodyBuilder.build(); + + Request.Builder builder = new Request.Builder().url(url).post(requestBody); + + // 添加header + addHeader(builder, header); + + Request request = builder.build(); + Response response = httpClient.newCall(request).execute(); + try { + return response.body().string(); + } finally { + response.close(); + } + } + + private void addHeader(Request.Builder builder, Map header) { + if (header != null) { + Set> entrySet = header.entrySet(); + for (Map.Entry entry : entrySet) { + builder.addHeader(entry.getKey(), String.valueOf(entry.getValue())); + } + } + } + + public void setCookieStore(Map> cookieStore) { + this.cookieStore = cookieStore; + } + + public void setHttpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + } + +} diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/UploadFile.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/UploadFile.java new file mode 100644 index 00000000..7045bc58 --- /dev/null +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/UploadFile.java @@ -0,0 +1,60 @@ +package com.gitee.sop.websiteserver.bean; + + +import com.gitee.sop.websiteserver.util.FileUtil; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.codec.digest.DigestUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; + +/** + * 文件上传类 + * @author tanghc + */ +@Getter +@Setter +public class UploadFile implements Serializable { + private static final long serialVersionUID = -1100614660944996398L; + + /** + * @param name 表单名称,不能重复 + * @param file 文件 + * @throws IOException + */ + public UploadFile(String name, File file) throws IOException { + this(name, file.getName(), FileUtil.toBytes(file)); + } + + /** + * @param name 表单名称,不能重复 + * @param fileName 文件名 + * @param input 文件流 + * @throws IOException + */ + public UploadFile(String name, String fileName, InputStream input) throws IOException { + this(name, fileName, FileUtil.toBytes(input)); + } + + /** + * @param name 表单名称,不能重复 + * @param fileName 文件名 + * @param fileData 文件数据 + */ + public UploadFile(String name, String fileName, byte[] fileData) { + super(); + this.name = name; + this.fileName = fileName; + this.fileData = fileData; + this.md5 = DigestUtils.md5Hex(fileData); + } + + private String name; + private String fileName; + private byte[] fileData; + private String md5; + +} diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java index d4a3331e..710b9fac 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java @@ -35,8 +35,11 @@ public class WebsiteConfig implements ApplicationRunner { return new HttpMessageConverters(converter); } + /** + * SpringBoot启动完毕执行 + */ @Override public void run(ApplicationArguments args) throws Exception { - docManager.load(); + docManager.load(null); } } diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java index 82410925..0e020f4b 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java @@ -70,7 +70,7 @@ public class DocController { public String reload(String pwd) { boolean correct = StringUtils.equals(this.pwd, pwd); if (correct) { - docManager.load(); + docManager.load(null); } return String.valueOf(correct); } diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java index 2d22ab6d..96ac2026 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java @@ -1,9 +1,13 @@ package com.gitee.sop.websiteserver.controller; +import com.gitee.sop.websiteserver.bean.HttpTool; +import com.gitee.sop.websiteserver.bean.UploadFile; import com.gitee.sop.websiteserver.sign.AlipayApiException; import com.gitee.sop.websiteserver.sign.AlipaySignature; +import com.gitee.sop.websiteserver.util.UploadUtil; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; @@ -13,18 +17,24 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; -import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -39,13 +49,16 @@ public class SandboxController { @Value("${api.url-sandbox}") private String url; + static HttpTool httpTool = new HttpTool(); + @RequestMapping("/test") public SandboxResult proxy( @RequestParam String appId , @RequestParam String privateKey , @RequestParam String method , @RequestParam String version - , @RequestParam String bizContent) throws AlipayApiException { + , @RequestParam String bizContent + , HttpServletRequest request) throws AlipayApiException { Assert.isTrue(StringUtils.isNotBlank(appId), "AppId不能为空"); Assert.isTrue(StringUtils.isNotBlank(privateKey), "PrivateKey不能为空"); @@ -81,9 +94,31 @@ public class SandboxController { params.put("sign", sign); - String responseData = get(url, params);// 发送请求 - result.apiResult = responseData; - return result; + Collection uploadFiles = UploadUtil.getUploadFiles(request); + List files = uploadFiles.stream() + .map(multipartFile -> { + try { + return new UploadFile(multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getBytes()); + } catch (IOException e) { + log.error("封装文件失败", e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + try { + String responseData; + if (!CollectionUtils.isEmpty(files)) { + responseData = httpTool.requestFile(url, params, Collections.emptyMap(), files); + } else { + responseData = httpTool.request(url, params, Collections.emptyMap(), "get"); + } + result.apiResult = responseData; + return result; + } catch (Exception e) { + throw new RuntimeException("请求失败"); + } } @Data @@ -95,6 +130,7 @@ public class SandboxController { private String apiResult; } + /** * 发送get请求 * @param url @@ -102,14 +138,10 @@ public class SandboxController { * @throws Exception */ public static String get(String url, Map params) { - CloseableHttpClient client = null; + CloseableHttpClient httpClient = null; CloseableHttpResponse response = null; try{ - /** - * 创建一个httpclient对象 - */ - client = HttpClients.createDefault(); - + httpClient = HttpClients.createDefault(); List nameValuePairs = params.entrySet() .stream() .map(entry -> new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))) @@ -129,7 +161,7 @@ public class SandboxController { /** * 执行post请求 */ - response = client.execute(get); + response = httpClient.execute(get); /** * 通过EntityUitls获取返回内容 */ @@ -137,7 +169,7 @@ public class SandboxController { }catch (Exception e){ e.printStackTrace(); }finally { - IOUtils.closeQuietly(client); + IOUtils.closeQuietly(httpClient); IOUtils.closeQuietly(response); } return null; diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java index b60eb46f..1deecbe4 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java @@ -10,7 +10,7 @@ import java.util.Collection; */ public interface DocManager { - void load(); + void load(String serviceId); DocItem get(String method, String version); diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java index 4837f7a3..11e8bb04 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java @@ -9,6 +9,7 @@ import com.gitee.sop.registryapi.service.RegistryService; import com.gitee.sop.websiteserver.bean.DocInfo; import com.gitee.sop.websiteserver.bean.DocItem; import com.gitee.sop.websiteserver.bean.ZookeeperContext; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.springframework.beans.factory.annotation.Autowired; @@ -17,6 +18,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; +import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; @@ -66,7 +68,7 @@ public class DocManagerImpl implements DocManager { private volatile boolean listenInited; @Override - public void load() { + public void load(String serviceId) { try { List serviceInfoList = registryService.listAllService(1, 9999); log.info("服务列表:{}", serviceInfoList); @@ -76,6 +78,12 @@ public class DocManagerImpl implements DocManager { // 网关没有文档提供,需要排除 .filter(serviceInfo -> !"API-GATEWAY".equalsIgnoreCase(serviceInfo.getServiceId())) .filter(serviceInfo -> !serviceInfo.getInstances().isEmpty()) + .filter(serviceInfo -> { + if (StringUtils.isEmpty(serviceId)) { + return true; + } + return serviceId.equalsIgnoreCase(serviceInfo.getServiceId()); + }) .map(serviceInfo -> serviceInfo.getInstances().get(0)) .collect(Collectors.toList()) .forEach(this::loadDocInfo); @@ -88,7 +96,7 @@ public class DocManagerImpl implements DocManager { String query = this.buildQuery(); String url = "http://" + serviceInstance.getIp() + ":" + serviceInstance.getPort() + "/v2/api-docs" + query; try { - log.info("读取swagger文档,url:{}", url); + log.info("读取swagger文档,serviceId:{}, url:{}", serviceInstance.getServiceId(), url); ResponseEntity entity = restTemplate.getForEntity(url, String.class); if (entity.getStatusCode() != HttpStatus.OK) { throw new IllegalAccessException("无权访问"); @@ -156,8 +164,15 @@ public class DocManagerImpl implements DocManager { ZookeeperContext.listenChildren(routeRootPath, 1, (client, event) -> { if (listenInited) { long id = System.currentTimeMillis(); + byte[] data = event.getData().getData(); + String serviceInfoJson = new String(data); + ZKServiceInfo serviceInfo = JSON.parseObject(serviceInfoJson, ZKServiceInfo.class); + String serviceId = serviceInfo.getServiceId(); + log.info("微服务[{}]推送更新", serviceId); + Msg msg = new Msg(id, 1000 * 20); + msg.serviceId = serviceId; // 延迟20秒执行 - queue.offer(new Msg(id, 1000 * 20)); + queue.offer(msg); } TreeCacheEvent.Type type = event.getType(); if (type == TreeCacheEvent.Type.INITIALIZED) { @@ -169,6 +184,7 @@ public class DocManagerImpl implements DocManager { static class Msg implements Delayed { private long id; private long delay; + private String serviceId; // 自定义实现比较方法返回 1 0 -1三个参数 @@ -192,6 +208,14 @@ public class DocManagerImpl implements DocManager { } } + @Data + static class ZKServiceInfo { + /** 服务名称,对应spring.application.name */ + private String serviceId; + + private String description; + } + @Slf4j static class Consumer implements Runnable { private DelayQueue queue; @@ -206,9 +230,9 @@ public class DocManagerImpl implements DocManager { public void run() { while (true) { try { - queue.take(); + Msg msg = queue.take(); log.info("延迟队列触发--重新加载文档信息"); - docManager.load(); + docManager.load(msg.serviceId); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java index 87c45764..533ec699 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java @@ -67,6 +67,7 @@ public class SwaggerDocParser implements DocParser { docItem.setVersion(docInfo.getString("sop_version")); docItem.setSummary(docInfo.getString("summary")); docItem.setDescription(docInfo.getString("description")); + docItem.setMultiple(docInfo.getString("multiple") != null); String moduleName = this.buildModuleName(docInfo, docRoot); docItem.setModule(moduleName); List docParameterList = this.buildRequestParameterList(docInfo, docRoot); diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/FileUtil.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/FileUtil.java new file mode 100644 index 00000000..ce4ea33e --- /dev/null +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/FileUtil.java @@ -0,0 +1,65 @@ +package com.gitee.sop.websiteserver.util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class FileUtil { + + /** + * The default buffer size to use. + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + private static final int EOF = -1; + + /** + * 将文件流转换成byte[] + * @param input + * @return + * @throws IOException + */ + public static byte[] toBytes(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int n = 0; + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + } + return output.toByteArray(); + } + + /** + * 将文件转换成数据流 + * @param file 文件 + * @return 返回数据流 + * @throws IOException + */ + public static byte[] toBytes(File file) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + if (file.canRead() == false) { + throw new IOException("File '" + file + "' cannot be read"); + } + } else { + throw new FileNotFoundException("File '" + file + "' does not exist"); + } + InputStream input = null; + try { + input = new FileInputStream(file); + return toBytes(input); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException ioe) { + } + } + } +} diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java new file mode 100644 index 00000000..565d4cf4 --- /dev/null +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java @@ -0,0 +1,38 @@ +package com.gitee.sop.websiteserver.util; + +import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +/** + * 文件上传工具类 + * + * @author tanghc + */ +public class UploadUtil { + + /** + * 获取上传文件 + * + * @param request + * @return + */ + public static Collection getUploadFiles(HttpServletRequest request) { + Map fileMap = null; + //检查form中是否有enctype="multipart/form-data" + if (ServletFileUpload.isMultipartContent(request)) { + //将request变成多部分request + MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; + fileMap = multiRequest.getFileMap(); + } + return Optional.ofNullable(fileMap) + .map(map -> map.values()) + .orElse(Collections.emptyList()); + } +}