diff --git a/README.md b/README.md index 8a644d483760664ec326e3e4cba5cab45cc7fc4d..f6d8e45b15a92dab0bf7f3ed3a0983841985c834 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.4 + 3.1.5 ``` @@ -53,7 +53,7 @@ public class HelloworldController extends JbootController { - [事件机制](./doc/docs/event.md) - [SPI扩展机制](./doc/docs/spi.md) - [代码生成器](./doc/docs/codegen.md) -- [项目构建](./doc/docs/build.md) +- [项目打包](./doc/docs/build.md) - [项目部署](./doc/docs/deploy.md) - [Jboot与Docker](./doc/docs/docker.md) - [1.x 升级到 2.x 教程](./doc/docs/upgrade.md) diff --git a/changes.txt b/changes.txt index 37e45638931d3e30af18d757fdc41b50d4b08238..577f3c8c1c4b11b7c49aa75b487a0dcc92c6bfba 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,18 @@ +jboot v3.1.5: +新增:新增 Motan RPC 的 export 和 host 的相关配置以及test代码 +新增:JbootJson 支持驼峰式 JsonKey 输出,感谢Gitee的 @herowjun +新增:新增 Controller 对返回值自动渲染的功能 +新增:JbootRPCConfig 新增默认 version 和 group 配置的支持 +优化:ClassScanner 添加无需扫码的 jar 排除,速度更快 +修复:ClassScanner 在 Windows 平台下可能存在重复扫码而拖慢启动速度的问题 +修复:门户网关 GatewayHttpProxy 在 POST 时的某些情况下会出现无法正确代理的问题 +文档:完善 RPC 配置的相关文档 +文档:完善 Gateway 配置的相关文档 +文档:添加 fatjar 打包的相关文档 +文档:完善 fatjar 部署运行的相关文档 + + + jboot v3.1.4: 优化:重构 ClassScanner ,提高在 fatjar 模式的扫描性能 优化:重构 ConfigManager ,以便更好的支持 fatjar 模式下的配置文件读取 diff --git a/doc/docs/aop.md b/doc/docs/aop.md index ec0c8868a99d927f07957a1128ca04976a43c040..0ec52a65b4ff9f2deaf013eff22a375b51ace21c 100644 --- a/doc/docs/aop.md +++ b/doc/docs/aop.md @@ -216,7 +216,7 @@ public class AppConfiguration { ``` -这样,在一个 Jboot 应用中,就会存在两份 `CommentService` 他们的名称分别为:myCommentServiceFromConfiguration 和 myCommentService2(当只用了注解 @Bean 但是为添加 name 参数时,名称为方法名) +这样,在一个 Jboot 应用中,就会存在两份 `CommentService` 他们的名称分别为:myCommentServiceFromConfiguration 和 myCommentService2(当只用了注解 @Bean 但是未添加 name 参数时,name 的值为方法的名称) 这样,我们就可以在 Controller 里,通过 `@Inject` 配合 `@Bean(name = ... )` 进行注入,例如: diff --git a/doc/docs/build.md b/doc/docs/build.md index 840323c013bf6e3249b9a2cceeadee3311c05377..afd0cecbf74b75d0a67163f4c3912107373ba714 100644 --- a/doc/docs/build.md +++ b/doc/docs/build.md @@ -1,11 +1,12 @@ -# 项目构建 +# 项目打包 ## 目录 -- 单模块 maven 项目构建 -- 多模块 maven 项目构建 +- 单模块 maven 项目打包 +- 多模块 maven 项目打包 +- fatjar 打包(全部打包到一个jar里) -## 单模块 maven 项目构建 +## 单模块 maven 项目打包 在单一模块的maven项目开发中,我们通常在 `src/main/resources` 编写我们的配置文件,因此,在 maven 构建的时候,我们需要添加如下配置: @@ -175,7 +176,7 @@ fi 复制该文件夹到服务器,然后执行里面的 `jboot.sh start` 命令即可上线。 -## 多模块 maven 项目构建 +## 多模块 maven 项目打包 多模块项目在以上配置的基础上,添加 `maven-resources-plugin` maven 插件,用于拷贝其他maven模块的资源文件和html等内容到此运行模块。 @@ -210,4 +211,102 @@ maven 配置如下: ``` -这部分可以参考 jpress 项目,网址:https://gitee.com/fuhai/jpress/blob/v2.0/starter/pom.xml \ No newline at end of file +这部分可以参考 jpress 项目,网址:https://gitee.com/fuhai/jpress/blob/v2.0/starter/pom.xml + +## fatjar 打包(全部打包到一个jar里) + +fatjar 打包指的是,把所有资源(html、css、js)以及项目依赖全部打包到一个 jar 包里,这样我们可以通过 +命令 `java -jar xxx.jar` 启动,更加方便部署,特别是方便在微服务下的多模块部署。 + +fatjar 打包第一步,在 pom.xml 添加如下配置 + +```xml + + + + src/main/resources + + **/*.* + + false + + + + src/main/webapp + + **/*.* + + false + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + -parameters + + + + + + maven-resources-plugin + + + copy-resources + validate + + copy-resources + + + ${basedir}/target/classes/webapp + + + ${basedir}/src/main/webapp + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + make-assembly + package + + single + + + + + io.jboot.app.JbootApplication + + + + + + jar-with-dependencies + + + + + + + +``` + +第二步:通过 Maven 打包 + +执行命令 `mvn clean package` 进行打包,在 pom.xml 对应的模块下会生成一个 xxx-with-dependencies.jar 的 jar 包,复制该 jar +到服务器上,执行 `java -jar xxx-with-dependencies.jar` 即可启动项目。 \ No newline at end of file diff --git a/doc/docs/deploy.md b/doc/docs/deploy.md index 6486679fcdfb59d1f45040b72a0ac510c9fd8daf..21a2448c8f2f1b615ed75cbee6cf636e280c8be1 100644 --- a/doc/docs/deploy.md +++ b/doc/docs/deploy.md @@ -2,15 +2,101 @@ ## 目录 - 描述 +- 通过 脚本 运行 - 通过 Jar 运行 - 通过 Tomcat 运行 ## 描述 +本文档提供了 3 种部署方式,对应 Jboot 里的 3 种[打包方式](./build.md)。 + +## 通过 脚本 运行 + +在 [打包方式](./build.md) 文档中,我们可以把项目打包成一个 .zip 的压缩包项目,里面带有 jboot.sh (和 jboot.bat) 执行脚本, +只需要我们解压 .zip 压缩文件,通过如下命令就可以对 jboot 项目进行启动和停止。 + +```shell script +# 启动 +./jboot.sh start + +# 停止 +./jboot.sh stop + +# 重启 +./jboot.sh restart +``` + +在 Windows 系统中,通过如下命令执行 + +```shell script +# 启动 +jboot.bat start + +# 停止 +jboot.bat stop + +# 重启 +jboot.bat restart +``` ## 通过 Jar 运行 -Jboot 通过依赖 `JFinal-Undertow` 内置了 Undertow 服务器,可以直接通过 Jar 的方式进行运行,这部分直接参考文档:https://www.jfinal.com/doc/1-3 即可。 +在 [打包方式](./build.md) 文档中,我们可以把所有的资源文件(html、css、js、配置文件 等)以及项目的所有依赖打包到一个 jar 包 +里去,打包成功后,可以通过如下命令运行。 + +启动(前台启动,命令窗口不能关闭) +```shell script +java -jar xxx.jar +``` +> 当前ssh窗口(命令窗口)被锁定,可按 `CTRL + C` 打断程序运行,或直接关闭窗口,程序退出。 + +启动(后台启动,命令窗口不能关闭) +```shell script +java -jar xxx.jar & +``` +> & 代表在后台运行。当前ssh窗口不被锁定,但是当窗口关闭时,程序中止运行。 + + +启动(后台启动) +```shell script +nohup java -jar xxx.jar & +``` +>nohup 意思是不挂断运行命令,当账户退出或终端关闭时,程序仍然运行,当用 nohup 命令执行作业时,缺省情况下该作业的所有输出被重定向到nohup.out的文件中,除非另外指定了输出文件。 + +启动(后台启动) +```shell script +nohup java -jar xxx.jar>temp.txt & +``` +>command >out.file 是将 command 的输出重定向到 out.file 文件,即输出内容不打印到屏幕上,而是输出到 out.file 文件中。 +>以上命令,就是把 java 启动的输出,输入到 temp.txt 文件里,而不是在屏幕上。 + + +另外:可以通过如下命令实时查看日志: + +```shell script +tail -f temp.text +``` + +可通过jobs命令查看后台运行任务 + +```shell script +jobs +``` + +jobs 命令就会列出所有后台执行的作业,并且每个作业前面都有个编号。如果想将某个作业调回前台控制,只需要 fg + 编号即可。 + +```shell script +fg 33 +``` + +查看程序端口的进程的pid + +```shell script +netstat -nlp | grep:8080 +``` + + + ## 通过 Tomcat 运行 diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index d5192ec0cef08941fe64623c23f6a1d840141eee..331836e26e96d786ea49a6482e5c7da503981686 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -6,6 +6,7 @@ - path路由 - host路由 - query路由 +- 多个 Gateway 配置 - 其他 @@ -187,6 +188,74 @@ jboot.gateway.queryContains = aaa 以上配置中,如果用户访问 `www.xxx.com/controller?aaa=bbb` 会自动路由到 `http://youdomain:8080/controller?aaa=bbb` ,或者用户访问 `www.xxx.com/controller?aaa=ccc` 也会路由到 `http://youdomain:8080/controller?aaa=ccc`,因为 query 都包含了 `aaa=**` 的请求,但是如果用户访问 `www.xxx.com/controller?other=aaa`不会路由。 + + +## 多个 Gateway 配置 + +``` +jboot.gateway.aaa.name = name +jboot.gateway.aaa.uri = http://youdomain:8080 +jboot.gateway.aaa.enable = true +jboot.gateway.aaa.sentinelEnable = false +jboot.gateway.aaa.sentinelBlockPage = /block +jboot.gateway.aaa.proxyReadTimeout = 10000 +jboot.gateway.aaa.proxyConnectTimeout = 5000 +jboot.gateway.aaa.proxyContentType = text/html;charset=utf-8 +jboot.gateway.aaa.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.aaa.pathEquals = /path +jboot.gateway.aaa.pathContains = /path +jboot.gateway.aaa.pathStartsWith = /path +jboot.gateway.aaa.pathEndswith = /path +jboot.gateway.aaa.hostEquals = xxx.com +jboot.gateway.aaa.hostContains = xxx.com +jboot.gateway.aaa.hostStartsWith = xxx.com +jboot.gateway.aaa.hostEndswith = xxx.com +jboot.gateway.aaa.queryEquals = aa:bb,cc:dd +jboot.gateway.aaa.queryContains = aa,bb + + +jboot.gateway.bbb.name = name +jboot.gateway.bbb.uri = http://youdomain:8080 +jboot.gateway.bbb.enable = true +jboot.gateway.bbb.sentinelEnable = false +jboot.gateway.bbb.sentinelBlockPage = /block +jboot.gateway.bbb.proxyReadTimeout = 10000 +jboot.gateway.bbb.proxyConnectTimeout = 5000 +jboot.gateway.bbb.proxyContentType = text/html;charset=utf-8 +jboot.gateway.bbb.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.bbb.pathEquals = /path +jboot.gateway.bbb.pathContains = /path +jboot.gateway.bbb.pathStartsWith = /path +jboot.gateway.bbb.pathEndswith = /path +jboot.gateway.bbb.hostEquals = xxx.com +jboot.gateway.bbb.hostContains = xxx.com +jboot.gateway.bbb.hostStartsWith = xxx.com +jboot.gateway.bbb.hostEndswith = xxx.com +jboot.gateway.bbb.queryEquals = aa:bb,cc:dd +jboot.gateway.bbb.queryContains = aa,bb + + +jboot.gateway.xxx.name = name +jboot.gateway.xxx.uri = http://youdomain:8080 +jboot.gateway.xxx.enable = true +jboot.gateway.xxx.sentinelEnable = false +jboot.gateway.xxx.sentinelBlockPage = /block +jboot.gateway.xxx.proxyReadTimeout = 10000 +jboot.gateway.xxx.proxyConnectTimeout = 5000 +jboot.gateway.xxx.proxyContentType = text/html;charset=utf-8 +jboot.gateway.xxx.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.xxx.pathEquals = /path +jboot.gateway.xxx.pathContains = /path +jboot.gateway.xxx.pathStartsWith = /path +jboot.gateway.xxx.pathEndswith = /path +jboot.gateway.xxx.hostEquals = xxx.com +jboot.gateway.xxx.hostContains = xxx.com +jboot.gateway.xxx.hostStartsWith = xxx.com +jboot.gateway.xxx.hostEndswith = xxx.com +jboot.gateway.xxx.queryEquals = aa:bb,cc:dd +jboot.gateway.xxx.queryContains = aa,bb +``` + ## 其他 当配置中,如果一个内容存在多个值的时候,需要用英文逗号(,)隔开。 @@ -202,4 +271,4 @@ jboot.gateway.pathContains = /user,/article 当 path 中,只要存在 `/user` 或者 存在 `/article` 都会匹配到该路由,比如 `www.xxx.com/user/xxx` 或者 `www.xxx.com/article/xxx` 都会匹配到。 -其他同理。 +其他同理。 \ No newline at end of file diff --git a/doc/docs/install.md b/doc/docs/install.md index aa964cedb199954c22a33a57c92ca63364bc2693..04152d49de476d4f0e878b15064ad66be118135e 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.4 + 3.1.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 82fb65b7273d61cc3dde6afbda0eec817503d35b..603ec0456dc2201feff2e04fd689d90cf9420b12 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.4 + 3.1.5 ``` diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index 1d0f327585e67e01271dc2d5aa173978d13e0b4b..677456d1751032d4cac975395a70ed3460d0eb4d 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -79,6 +79,10 @@ jboot.rpc.type = dubbo jboot.rpc.urls = com.yourdomain.AAAService:127.0.0.1:8080,com.yourdomain.XXXService:127.0.0.1:8080 jboot.rpc.providers = com.yourdomain.AAAService:providerName,com.yourdomain.XXXService:providerName jboot.rpc.consumers = com.yourdomain.AAAService:consumerName,com.yourdomain.XXXService:consumerName +jboot.rpc.defaultVersion = 1.0.0 +jboot.rpc.versions = com.yourdomain.AAAService:1.0.0,com.yourdomain.XXXService:1.0.1 +jboot.rpc.defaultGroup = +jboot.rpc.groups = com.yourdomain.AAAService:group1,com.yourdomain.XXXService:group2 jboot.rpc.autoExportEnable = true ``` @@ -86,6 +90,10 @@ jboot.rpc.autoExportEnable = true - jboot.rpc.urls : 一般不用配置,只有直连模式下才会去配置,此处是配置 Service接口和URL地址的映射关系。 - jboot.rpc.providers : 配置 Service 和 Provider 的映射关系( Motan下配置的是 Service 和 BasicService 的映射关系)。 - jboot.rpc.consumers : 配置 Reference 和 consumer 的映射关系( Motan下配置的是 Referer 和 BaseReferer 的映射关系)。 +- jboot.rpc.defaultVersion : 当service不配置版本时,默认的版本号,默认值为 1.0.0 +- jboot.rpc.versions : 每个服务对应的版本号 +- jboot.rpc.defaultGroup : 当服务不配置 group 时,默认的 gourp +- jboot.rpc.groups : 每个服务对应的 group - jboot.rpc.autoExportEnable : 当 Jboot 启动的时候,是否自动暴露 @RPCBean 注解的接口。 diff --git a/pom.xml b/pom.xml index 176a2ff2feacc07071c16a20960e7f3381525b5b..282ffdd2ef3a216c99b29f329ac2797223370e94 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.5-SNAPSHOT + 3.1.6-SNAPSHOT jar jboot @@ -59,17 +59,17 @@ 1.13.1 2.10.6 2.8.1 - 3.9 + 3.10 2.6 1.14 4.5.12 4.4.13 4.1.48.Final 1.7.1 - 2.7.5 + 2.7.6 3.11.0.Final 1.1.0 - 1.1.7 + 1.1.8 4.1.5 1.8 @@ -275,7 +275,7 @@ com.alibaba.nacos nacos-client - 1.2.0 + 1.2.1 provided diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 28a9284b5211674d880b47b757dd979176e4de03..7d120266cd24bcfd4fc7235b9c75e5249130cdbf 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.4"; + public static String VERSION = "3.1.5"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 5c4c53f25af63154a3fcd7f29dbfaafa32b8dd51..eabd9f87849b1c0bf476f00b82d9cc6f168b1ea0 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -81,12 +81,17 @@ public class GatewayHttpProxy { */ configConnection(conn, req); - conn.connect(); - /** - * 复制 post 请求内容到目标服务器 - */ - copyRequestStreamToConnection(req, conn); + // get 请求 + if ("get".equalsIgnoreCase(req.getMethod())) { + conn.connect(); + } + // post 请求 + else { + conn.setDoOutput(true); + conn.setDoInput(true); + copyRequestStreamToConnection(req, conn); + } /** @@ -95,7 +100,7 @@ public class GatewayHttpProxy { configResponse(resp, conn); /** - * 复制目标流到 Response + * 复制目标相应流到 Response */ copyStreamToResponse(conn, resp); @@ -111,19 +116,12 @@ public class GatewayHttpProxy { OutputStream outStream = null; InputStream inStream = null; try { - - // 如果不是 post 请求,不需要复制 - if ("get".equalsIgnoreCase(req.getMethod())) { - return; - } - - conn.setDoOutput(true); outStream = conn.getOutputStream(); inStream = req.getInputStream(); - int n; + int len; byte[] buffer = new byte[1024]; - while (-1 != (n = inStream.read(buffer))) { - outStream.write(buffer, 0, n); + while ((len = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); } } finally { @@ -149,7 +147,6 @@ public class GatewayHttpProxy { } finally { quetlyClose(inStream, reader); } - } @@ -174,15 +171,22 @@ public class GatewayHttpProxy { Set headerNames = headerFields.keySet(); for (String headerName : headerNames) { //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 - if (StrUtil.isNotBlank(headerName) && !"Content-Encoding".equalsIgnoreCase(headerName)) { - resp.setHeader(headerName, conn.getHeaderField(headerName)); + //需要排除 Content-Type,因为会可能会进行多次设置 + if (StrUtil.isBlank(headerName) + || "Content-Encoding".equalsIgnoreCase(headerName) + || "Content-Type".equalsIgnoreCase(headerName)) { + continue; + } else { + String headerFieldValue = conn.getHeaderField(headerName); + if (StrUtil.isNotBlank(headerFieldValue)) { + resp.setHeader(headerName, headerFieldValue); + } } } } } private static InputStream getInputStream(HttpURLConnection conn) throws IOException { - InputStream stream = conn.getResponseCode() >= 400 ? conn.getErrorStream() : conn.getInputStream(); @@ -192,7 +196,6 @@ public class GatewayHttpProxy { } else { return stream; } - } @@ -200,16 +203,19 @@ public class GatewayHttpProxy { conn.setReadTimeout(readTimeOut); conn.setConnectTimeout(connectTimeOut); - conn.setInstanceFollowRedirects(true); + conn.setInstanceFollowRedirects(false); conn.setUseCaches(false); -// conn.setDefaultUseCaches(); conn.setRequestMethod(req.getMethod()); + Enumeration headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); if (StrUtil.isNotBlank(headerName)) { - conn.setRequestProperty(headerName, req.getHeader(headerName)); + String headerFieldValue = req.getHeader(headerName); + if (StrUtil.isNotBlank(headerFieldValue)) { + conn.setRequestProperty(headerName, headerFieldValue); + } } } } diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java index 7e7de2b8cc4b2fcbf62036e94babac10e5f172fb..5aefb152b55a1f3fef872d69483703846c5041c3 100644 --- a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -37,7 +37,7 @@ public class GatewaySentinelProcesser { Entry entry = null; String resourceName = GatewayUtil.buildResource(req); try { - entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); + entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_API_GATEWAY, EntryType.IN); runnable.run(); } catch (BlockException ex) { processBlocked(config, req, resp); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 048dfc7cac019e586609d05a208e692fb756db43..6b89656c43faea9fb90d2cab31d933c4de0d1b97 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -15,6 +15,7 @@ */ package io.jboot.components.gateway; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -233,6 +234,7 @@ public class JbootGatewayConfig implements Serializable { private GatewayInterceptor[] inters; + public GatewayInterceptor[] getInters() { if (interceptors == null || interceptors.length == 0) { return null; @@ -255,8 +257,28 @@ public class JbootGatewayConfig implements Serializable { } + private Boolean configOk = null; + public boolean isConfigOk() { - return StrUtil.isNotBlank(uri); + if (configOk != null) { + return configOk; + } + synchronized (this) { + if (configOk == null) { + configOk = StrUtil.isNotBlank(uri); + if (configOk) { + ensureUriPatterCorrect(); + } + } + } + return configOk; + } + + private void ensureUriPatterCorrect() { + if (!uri.toLowerCase().startsWith("http://") + && !uri.toLowerCase().startsWith("https://")) { + throw new JbootIllegalConfigException("gateway uri must start with http:// or https://"); + } } diff --git a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java b/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java deleted file mode 100644 index 1ba7e278ce4abd44c979a88f3ed9db64c20d3604..0000000000000000000000000000000000000000 --- a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.ActionException; -import com.jfinal.log.Log; -import com.jfinal.render.Render; -import com.jfinal.render.RenderException; -import com.jfinal.render.RenderManager; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; -import io.jboot.components.restful.exception.RequestMethodErrorException; -import io.jboot.web.handler.JbootActionHandler; - -import java.io.Serializable; - -/** - * 默认的restful错误响应处理器 - */ -public class DefaultRestfulErrorRender extends RestfulErrorRender { - - private static final Log log = Log.getLog(JbootActionHandler.class); - - public static class Error implements Serializable { - private String errorClass; - private int code; - private String message; - - public Error(String errorClass, int code, String message) { - this.errorClass = errorClass; - this.code = code; - this.message = message; - } - - public String getErrorClass() { - return errorClass; - } - - public Error setErrorClass(String errorClass) { - this.errorClass = errorClass; - return this; - } - - public int getCode() { - return code; - } - - public Error setCode(int code) { - this.code = code; - return this; - } - - public String getMessage() { - return message; - } - - public Error setMessage(String message) { - this.message = message; - return this; - } - } - - public void render() { - log.error("The restful handler intercepted the error", super.getError()); - Error error = null; - if(super.getError() instanceof ParameterNullErrorException - || super.getError() instanceof ParameterParseErrorException){ - //400 - error = new Error(super.getError().getClass().getName(), - HttpStatus.BAD_REQUEST.value(), super.getError().getMessage()); - } else if(super.getError() instanceof RequestMethodErrorException){ - error = new Error(super.getError().getClass().getName(), - HttpStatus.METHOD_NOT_ALLOWED.value(), super.getError().getMessage()); - } else if(super.getError() instanceof ActionException){ - //解析错误代码 - ActionException actionException = (ActionException)super.getError(); - int errorCode = actionException.getErrorCode(); - String msg = ""; - if (errorCode == 404) { - msg = HttpStatus.NOT_FOUND.getReasonPhrase(); - } else if (errorCode == 400) { - msg = HttpStatus.BAD_REQUEST.getReasonPhrase(); - } else if (errorCode == 401) { - msg = HttpStatus.UNAUTHORIZED.getReasonPhrase(); - } else if (errorCode == 403) { - msg = HttpStatus.FORBIDDEN.getReasonPhrase(); - } else if(errorCode == 405){ - msg = HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase(); - } else { - msg = super.getError().getMessage(); - } - error = new Error(super.getError().getClass().getName(), - errorCode, msg); - } else if(super.getError() instanceof RenderException){ - //500 - error = new Error(super.getError().getClass().getName(), - HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); - } else { - //500 - error = new Error(super.getError().getClass().getName(), - HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); - } - Render jsonRender = RenderManager.me().getRenderFactory().getJsonRender(error); - jsonRender.setContext(super.request, super.response, super.getAction() == null? "" : super.getAction().getViewPath()); - super.response.setStatus(error.getCode()); - jsonRender.render(); - } - - - -} diff --git a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java index 4a2f1ed57760e861e581015c08d7bf74e8bd9b14..0f8808c2b0ff4c5f1fae9a1a94cf3712ca2bdeef 100644 --- a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java +++ b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java @@ -3,14 +3,16 @@ package io.jboot.components.restful; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; import com.jfinal.config.Routes; +import com.jfinal.core.ActionException; import com.jfinal.core.Controller; import com.jfinal.core.NotAction; +import com.jfinal.render.RenderManager; import io.jboot.components.restful.annotation.DeleteMapping; import io.jboot.components.restful.annotation.GetMapping; import io.jboot.components.restful.annotation.PostMapping; import io.jboot.components.restful.annotation.PutMapping; -import io.jboot.components.restful.exception.RequestMethodErrorException; import io.jboot.utils.StrUtil; +import io.jboot.web.HttpStatus; import io.jboot.web.controller.annotation.RequestMapping; import java.lang.reflect.Method; @@ -22,6 +24,7 @@ import java.util.Map; public class JbootRestfulManager { public static class Config { + private boolean mappingSupperClass; private String baseViewPath; private Interceptor[] routeInterceptors; @@ -81,7 +84,7 @@ public class JbootRestfulManager { protected static final String SLASH = "/"; - private RestfulErrorRender restfulErrorRender = new DefaultRestfulErrorRender(); + private RenderManager renderManager = RenderManager.me(); public static JbootRestfulManager me() { return me; @@ -177,10 +180,8 @@ public class JbootRestfulManager { } } RestfulAction action = new RestfulAction(baseRequestMapping, actionKey, controllerClass, - method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath())); + method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath()), requestMethod); String key = requestMethod + ":" + actionKey; - -// RestfulAction restfulAction = new RestfulAction(action, actionKey, requestMethod); if (restfulActions.put(key, action) != null) { //已经存在指定的key throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); @@ -205,34 +206,25 @@ public class JbootRestfulManager { //先直接获取 RestfulAction restfulAction = restfulActions.get(actionKey); if (restfulAction == null) { - //路径判断 - String[] paths = actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); - for (String _actionKey : restfulActions.keySet()) { - String _requestMethod = _actionKey.split(":")[0]; - String _target = _actionKey.split(":")[1]; - System.out.println("---------> target:"+target+",_target:"+_target+",_requestMethod:"+_requestMethod+",requestMethod:" + requestMethod); - if( target.equals(_target) && !_requestMethod.equals(requestMethod) ){ + String[] paths = target.split(SLASH); + for(Map.Entry entry : restfulActions.entrySet()){ + String _requestMethod = entry.getValue().getRequestMethod(); + String _target = entry.getValue().getActionKey(); + if (target.equals(_target) && !_requestMethod.equals(requestMethod)) { //请求方法不正确 - throw new RequestMethodErrorException(_actionKey, _requestMethod, target, requestMethod); + throw new ActionException(HttpStatus.METHOD_NOT_ALLOWED.value(), + renderManager.getRenderFactory().getErrorRender(HttpStatus.METHOD_NOT_ALLOWED.value()), + "'" + target + "' is specified as a '" + _requestMethod + "' request. '" + requestMethod + "' requests are not supported"); } - String[] _paths = _actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); - if (_actionKey.startsWith(requestMethod) && - _actionKey.contains("{") && _actionKey.contains("}") + String[] _paths = entry.getValue().getActionKey().split(SLASH); + if (entry.getValue().getActionKey().startsWith(requestMethod) && + entry.getValue().getActionKey().contains("{") && entry.getValue().getActionKey().contains("}") && paths.length == _paths.length && RestfulUtils.comparePaths(_paths, paths)) { - restfulAction = restfulActions.get(_actionKey); + restfulAction = entry.getValue(); break; } } } return restfulAction; } - - public RestfulErrorRender getRestfulErrorRender() { - return restfulErrorRender; - } - - public JbootRestfulManager setRestfulErrorRender(RestfulErrorRender restfulErrorRender) { - this.restfulErrorRender = restfulErrorRender; - return this; - } } diff --git a/src/main/java/io/jboot/components/restful/ResponseEntity.java b/src/main/java/io/jboot/components/restful/ResponseEntity.java deleted file mode 100644 index 90aad64467c540f9f4a3c0f682e5ad41c8e432ed..0000000000000000000000000000000000000000 --- a/src/main/java/io/jboot/components/restful/ResponseEntity.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.jboot.components.restful; - -import java.util.HashMap; -import java.util.Map; - -public class ResponseEntity { - - //响应的数据 - private T data; - - //自定义响应头部信息 - private Map headers = new HashMap<>(); - - //默认http状态 - private HttpStatus httpStatus = HttpStatus.OK; - - public T getData() { - return data; - } - - public ResponseEntity setData(T data) { - this.data = data; - return this; - } - - public ResponseEntity addHeaders(Map headers) { - this.headers.putAll(headers); - return this; - } - - public ResponseEntity addHeader(String key, String value) { - this.headers.put(key, value); - return this; - } - - public Map getHeaders() { - return headers; - } - - public ResponseEntity setHttpStatus(HttpStatus httpStatus) { - this.httpStatus = httpStatus; - return this; - } - - public HttpStatus getHttpStatus() { - return httpStatus; - } - - public ResponseEntity() { - } - - public ResponseEntity(T data, Map headers, HttpStatus httpStatus) { - this.data = data; - this.headers = headers; - this.httpStatus = httpStatus; - } - - public ResponseEntity(Map headers, HttpStatus httpStatus) { - this.headers = headers; - this.httpStatus = httpStatus; - } - - public ResponseEntity(T data) { - this.data = data; - } - -} diff --git a/src/main/java/io/jboot/components/restful/RestfulAction.java b/src/main/java/io/jboot/components/restful/RestfulAction.java index 7c2ccd4e6576b621142bf1c93797035391b98a64..1178f8c8f4903c5d817cc6cb71256ea320c5c997 100644 --- a/src/main/java/io/jboot/components/restful/RestfulAction.java +++ b/src/main/java/io/jboot/components/restful/RestfulAction.java @@ -8,7 +8,17 @@ import java.lang.reflect.Method; public class RestfulAction extends Action { - public RestfulAction(String controllerKey, String actionKey, Class controllerClass, Method method, String methodName, Interceptor[] interceptors, String viewPath) { + private String requestMethod; + + public String getRequestMethod() { + return requestMethod; + } + + public RestfulAction(String controllerKey, String actionKey, Class controllerClass, + Method method, String methodName, Interceptor[] interceptors, String viewPath, String requestMethod) { super(controllerKey, actionKey, controllerClass, method, methodName, interceptors, viewPath); + this.requestMethod = requestMethod; } + + } diff --git a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java b/src/main/java/io/jboot/components/restful/RestfulErrorRender.java deleted file mode 100644 index d2154da2fc9440b60dd9061ed96e14016f4965dc..0000000000000000000000000000000000000000 --- a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.Action; -import com.jfinal.render.Render; - - -public abstract class RestfulErrorRender extends Render { - - private String target; - - private Action action; - - private Exception error; - - public void init (String target, Action action, Exception error) { - this.target = target; - this.action = action; - this.error = error; - } - - protected RestfulErrorRender() { - } - - protected String getTarget() { - return target; - } - - protected Action getAction() { - return action; - } - - protected Exception getError() { - return error; - } -} diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java index a763739322469c0d36f5b0fb1a433de590d62c2d..58dcaeffc6283d81c6fbae9f660024ecf8824b2a 100644 --- a/src/main/java/io/jboot/components/restful/RestfulHandler.java +++ b/src/main/java/io/jboot/components/restful/RestfulHandler.java @@ -7,11 +7,11 @@ import com.jfinal.log.Log; import io.jboot.components.restful.annotation.ResponseHeader; import io.jboot.components.restful.annotation.ResponseHeaders; import io.jboot.utils.ArrayUtil; -import io.jboot.utils.StrUtil; import io.jboot.web.handler.JbootActionHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; public class RestfulHandler extends JbootActionHandler { @@ -19,15 +19,11 @@ public class RestfulHandler extends JbootActionHandler { @Override public Action getAction(String target, String[] urlPara, HttpServletRequest request) { - Action action = super.getAction(target, urlPara); + //优先从restful action 获取请求,防止url被当成urlPara处理 + Action action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); if (action == null) { - if (action.getActionKey().equals("/") && action.getMethodName().equals("index") - && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { - action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); - } + action = super.getAction(target, urlPara); } - - return action; } @@ -42,161 +38,6 @@ public class RestfulHandler extends JbootActionHandler { } } - - // @Override -// public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { -// if (target.indexOf('.') != -1) { -// return; -// } -// -// isHandled[0] = true; -// String[] urlPara = {null}; -// Action action = getAction(target, urlPara); -// RestfulAction restfulAction = null; -// if (action != null && action.getActionKey().equals("/") && action.getMethodName().equals("index") -// && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { -// //如果被默认的"/"拦截,并且为index方法,但是请求的url又和actionKey不匹配,则有可能是restful请求 -// try { -// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); -// } catch (RequestMethodErrorException e) { -// handleActionException(target, request, response, action, e); -// return; -// } -// if (restfulAction != null) { -// action = null; -// } -// } -// //如果无法从内置的action中获取action则尝试从restful管理的action中获取 -// if (action == null) { -// // 尝试从restful 获取action -// try { -// if (restfulAction == null) { -// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); -// } -// } catch (RequestMethodErrorException e) { -// handleActionException(target, request, response, action, e); -// return; -// } -// if (restfulAction != null) { -// //restful 风格的请求处理 -// restfulRequest(restfulAction, target, request, response, isHandled); -// return; -// } -// if (log.isWarnEnabled()) { -// String qs = request.getQueryString(); -// log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); -// } -// renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); -// return; -// } -// //在获取到了 -// super.handle(target, request, response, isHandled); -// } - -// private void restfulRequest(RestfulAction restfulAction, String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { -// Action action = restfulAction.getAction(); -// Controller controller = null; -// try { -// controller = controllerFactory.getController(action.getControllerClass()); -// //注入依赖 -// if (injectDependency) { -// com.jfinal.aop.Aop.inject(controller); -// } -// //绑定controller本身到当前线程 -// JbootControllerContext.hold(controller); -// //初始化controller -// CPI._init_(controller, action, request, response, null); -// -// //解析参数 -// Object[] args = RestfulUtils.parseActionMethodParameters(target, restfulAction.getActionKey(), -// action.getMethod(), request, controller.getRawData()); -// RestfulCallback restfulCallback = new RestfulCallback(restfulAction, controller); -// -// Invocation invocation = new Invocation(controller, action.getMethod(), action.getInterceptors(), -// restfulCallback, args); -// -// RestfulInvocation fixedInvocation; -// if (devMode) { -// if (ActionReporter.isReportAfterInvocation(request)) { -// fixedInvocation = invokeInvocation(invocation); -// ActionReporter.report(target, controller, action); -// } else { -// ActionReporter.report(target, controller, action); -// fixedInvocation = invokeInvocation(invocation); -// } -// } else { -// fixedInvocation = invokeInvocation(invocation); -// } -// -// Object returnValue = fixedInvocation.getReturnValue(); -// -// // 判断是否带有@DownloadResponse -// DownloadResponse downloadResponse = action.getMethod().getAnnotation(DownloadResponse.class); -// if (returnValue == null && downloadResponse == null) { -// //无返回结果的 restful 请求 -// controller.renderNull(); -// } -// // 如果标记了@DownloadResponse,并且返回值不为空,会被认为自行处理了下载行为 -// if (downloadResponse != null) { -// return; -// } -// if (returnValue != null) { -// //初始化返回值 -// initRenderValue(returnValue, controller); -// } -// -// Render render = controller.getRender(); -// if (render instanceof ForwardActionRender) { -// String actionUrl = ((ForwardActionRender) render).getActionUrl(); -// if (target.equals(actionUrl)) { -// throw new RuntimeException("The forward action url is the same as before."); -// } else { -// handle(actionUrl, request, response, isHandled); -// } -// return; -// } -// -// if (render == null) { -// render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); -// } -// -// //初始化自定义头部 -// initResponseHeaders(response, fixedInvocation.getMethod()); -// //响应开始 -// render.setContext(request, response, action.getViewPath()).render(); -// -// } catch (RenderException | ParameterNullErrorException | ParameterParseErrorException | ActionException e) { -// handleActionException(target, request, response, action, e); -// } catch (Exception e) { -// String info = ClassUtil.buildMethodString(action.getMethod()); -// if (log.isErrorEnabled()) { -// String qs = request.getQueryString(); -// String targetInfo = qs == null ? target : target + "?" + qs; -// log.error(info + " : " + targetInfo, e); -// } -// //自定义错误处理 -// handleActionException(target, request, response, action, e); -// } finally { -// JbootControllerContext.release(); -// controllerFactory.recycle(controller); -// } -// } - -// private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, -// Action action, Exception e) { -// RestfulErrorRender restfulErrorRender = JbootRestfulManager.me().getRestfulErrorRender(); -// restfulErrorRender.setContext(request, response, action == null ? "" : action.getViewPath()); -// restfulErrorRender.init(target, action, e); -// restfulErrorRender.render(); -// } - - -// protected RestfulInvocation invokeInvocation(Invocation inv) { -// RestfulInvocation fixedInvocation = new RestfulInvocation(inv); -// fixedInvocation.invoke(); -// return fixedInvocation; -// } - @Override public void setResponse(HttpServletResponse response, Action action) { ResponseHeader[] responseHeaders = action.getMethod().getAnnotationsByType(ResponseHeader.class); @@ -209,39 +50,10 @@ public class RestfulHandler extends JbootActionHandler { } } if (responseHeaders.length > 0) { - for (ResponseHeader header : responseHeaders) { + Arrays.asList(responseHeaders).forEach((ResponseHeader header) -> { response.setHeader(header.key(), header.value()); - } + }); } } -// protected void initRenderValue(Object o, Controller controller) { -// if (o.getClass().equals(String.class) -// || o.getClass().equals(int.class) -// || o.getClass().equals(double.class) -// || o.getClass().equals(byte.class) -// || o.getClass().equals(long.class) -// || o.getClass().equals(float.class) -// || o.getClass().equals(short.class) -// || o.getClass().equals(char.class) -// || o.getClass().equals(boolean.class)) { -// controller.renderText(String.valueOf(o)); -// } else if (o.getClass().equals(File.class)) { -// controller.renderFile((File) o); -// } else if (o.getClass().equals(ResponseEntity.class)) { -// ResponseEntity responseEntity = (ResponseEntity) o; -// //设置自定义头部信息 -// Map headers = responseEntity.getHeaders(); -// -// headers.forEach((k, v) -> controller.getResponse().setHeader(k, v)); -// //设置http状态代码 -// controller.getResponse().setStatus(responseEntity.getHttpStatus().value()); -// initRenderValue(responseEntity.getData(), controller); -// } else if (o instanceof Render) { //如果是render类型直接设置render -// controller.render((Render) o); -// } else { -// controller.renderJson(o); -// } -// } - } diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java index fe0df0d6d2c0902259c40e497827a3f8360e54c2..99e16b587c126eb86e8e500b9a255e4f0a81538b 100644 --- a/src/main/java/io/jboot/components/restful/RestfulInvocation.java +++ b/src/main/java/io/jboot/components/restful/RestfulInvocation.java @@ -8,7 +8,6 @@ public class RestfulInvocation extends Invocation { private Action action; - public RestfulInvocation(Action action, Controller controller, Object[] args) { super(controller, action.getMethod(), action.getInterceptors(), new RestfulCallback(action, controller), args); this.action = action; diff --git a/src/main/java/io/jboot/components/restful/RestfulUtils.java b/src/main/java/io/jboot/components/restful/RestfulUtils.java index cbb64f62f50cc53bc5034d4131e376afcbf0cad0..7fc18ef51870b36ee908ce14c667c6d30b97cfe4 100644 --- a/src/main/java/io/jboot/components/restful/RestfulUtils.java +++ b/src/main/java/io/jboot/components/restful/RestfulUtils.java @@ -1,23 +1,28 @@ package io.jboot.components.restful; +import com.jfinal.core.ActionException; import com.jfinal.kit.JsonKit; -import io.jboot.components.restful.annotation.PathVariable; -import io.jboot.components.restful.annotation.RequestBody; -import io.jboot.components.restful.annotation.RequestHeader; -import io.jboot.components.restful.annotation.RequestParam; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; +import com.jfinal.render.RenderManager; +import io.jboot.components.restful.annotation.*; import io.jboot.utils.StrUtil; +import io.jboot.web.HttpStatus; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.TimeZone; public class RestfulUtils { + private static final RenderManager renderManager = RenderManager.me(); + /** * 从url中解析路径参数 * @@ -52,11 +57,10 @@ public class RestfulUtils { * @param request * @param rawData * @return - * @throws ParameterNullErrorException - * @throws ParameterParseErrorException + * @throws ActionException */ public static Object[] parseActionMethodParameters(String target, String actionKey, Method actionMethod, HttpServletRequest request, String rawData) - throws ParameterNullErrorException, ParameterParseErrorException { + throws ActionException { Object[] args = new Object[actionMethod.getParameters().length]; for (int i = 0; i < actionMethod.getParameters().length; i++) { Parameter parameter = actionMethod.getParameters()[i]; @@ -72,28 +76,28 @@ public class RestfulUtils { } values = request.getParameterValues(parameterName); parameter.getType(); - args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType()); + args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType(), parameter); if (args[i] == null && requestParam.required()) { - //要求参数为空,但是却并没有提供参数 - throw new ParameterNullErrorException(parameterName); + //要求参数不为空,但是却并没有提供参数 + throw genBindError("Parameter '" + parameterName + "' specifies a forced check, but the value is null"); } } else if (requestBody != null) { - args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType()); + args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType(), parameter); } else if (requestHeader != null) { if (StrUtil.isNotBlank(requestHeader.value())) { parameterName = requestHeader.value(); } String value = request.getHeader(parameterName); - args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType()); + args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType(), parameter); if (args[i] == null && requestHeader.required()) { - //要求参数为空,但是却并没有提供参数 - throw new ParameterNullErrorException(parameterName); + //要求参数不为空,但是却并没有提供参数 + throw genBindError("Parameter '" + parameterName + "' specifies a forced check, but the value is null"); } } else if (pathVariable != null) { if (StrUtil.isNotBlank(pathVariable.value())) { parameterName = pathVariable.value(); } - args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType()); + args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType(), parameter); } else { args[i] = null; } @@ -119,83 +123,107 @@ public class RestfulUtils { return matchingCount == sourcePaths.length; } - private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass) { - if(parameterTypeClass.isArray()){ - Object [] objects = new Object[value.length]; + private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass, Parameter parameter) { + if (parameterTypeClass.isArray()) { + Object[] objects = new Object[value.length]; for (int i = 0; i < value.length; i++) { - objects[i] = parseCommonValue(value[i], name, parameterTypeClass); + objects[i] = parseCommonValue(value[i], name, parameterTypeClass, parameter); } return objects; } else { - if(value != null && value.length > 0){ - return parseCommonValue(value[0], name, parameterTypeClass); + if (value != null && value.length > 0) { + return parseCommonValue(value[0], name, parameterTypeClass, parameter); } } return null; } - private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass) { - return parseCommonValue(header, name, parameterTypeClass); + private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass, Parameter parameter) { + return parseCommonValue(header, name, parameterTypeClass, parameter); } - private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass) { + private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass, Parameter parameter) { //先当作基本数据来转换 - Object value = parseCommonValue(body, name, parameterTypeClass); - if(value == null){ + Object value = parseCommonValue(body, name, parameterTypeClass, parameter); + if (value == null) { value = JsonKit.parse(body, parameterTypeClass); } return value; } - private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass) { + private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass, Parameter parameter) { Map pathVariables = parsePathVariables(target, actionKey); String value = pathVariables.get(parameterName); - return parseCommonValue(value, parameterName, parameterTypeClass); + return parseCommonValue(value, parameterName, parameterTypeClass, parameter); } /** * 转换基本类型参数,目前支持string,int,double,float,boolean,long基本类型数据 + * * @param value * @param name * @param parameterTypeClass + * @param parameter * @return */ - private static Object parseCommonValue(String value, String name, Class parameterTypeClass) { + private static Object parseCommonValue(String value, String name, Class parameterTypeClass, Parameter parameter) { if (StrUtil.isBlank(value)) { return null; } if (parameterTypeClass.equals(String.class)) { return value; - } else if (parameterTypeClass.equals(int.class)) { - try { - return Integer.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(double.class)) { - try { - return Double.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(float.class)) { + } else if (parameterTypeClass.equals(int.class) + || parameterTypeClass.equals(double.class) + || parameterTypeClass.equals(float.class) + || parameterTypeClass.equals(long.class) + || parameterTypeClass.equals(BigDecimal.class) + || parameterTypeClass.equals(short.class)) { try { - return Float.valueOf(value); + if (parameterTypeClass.equals(int.class)) { + return Integer.valueOf(value); + } else if (parameterTypeClass.equals(double.class)) { + return Double.valueOf(value); + } else if (parameterTypeClass.equals(float.class)) { + return Float.valueOf(value); + } else if (parameterTypeClass.equals(long.class)) { + return Long.valueOf(value); + } else if (parameterTypeClass.equals(BigDecimal.class)) { + return new BigDecimal(value); + } else if (parameterTypeClass.equals(short.class)) { + return Short.valueOf(value); + } else { + return null; + } } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); + throw genBindError("Error resolving parameter '" + name + "', unable to match value '" + + value + "' to specified type '" + parameterTypeClass.getName() + "'"); } } else if (parameterTypeClass.equals(boolean.class)) { return Boolean.valueOf(value); - } else if (parameterTypeClass.equals(long.class)) { + } else if (parameterTypeClass.equals(Date.class)) { + DateFormat dateFormat = parameter.getAnnotation(DateFormat.class); + SimpleDateFormat simpleDateFormat; + if (dateFormat != null) { + simpleDateFormat = new SimpleDateFormat(dateFormat.value()); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone(dateFormat.timeZone())); + } else { + simpleDateFormat = new SimpleDateFormat(); + } try { - return Long.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); + return simpleDateFormat.parse(value); + } catch (ParseException e) { + throw genBindError("Error resolving parameter '" + name + "', unable to match value '" + + value + "' to specified type '" + parameterTypeClass.getName() + "'"); } } else { return null; } } + private static ActionException genBindError(String message) { + return new ActionException(HttpStatus.BAD_REQUEST.value(), + renderManager.getRenderFactory().getErrorRender(HttpStatus.BAD_REQUEST.value()), message); + } + } diff --git a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java b/src/main/java/io/jboot/components/restful/annotation/DateFormat.java similarity index 36% rename from src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java rename to src/main/java/io/jboot/components/restful/annotation/DateFormat.java index c4aa83e81af1d5259ab1797017e80dc1dac13178..b8939ecfff34a25e12be585e93135ed49c935337 100644 --- a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java +++ b/src/main/java/io/jboot/components/restful/annotation/DateFormat.java @@ -3,10 +3,17 @@ package io.jboot.components.restful.annotation; import java.lang.annotation.*; /** - * 标注controller action是一个下载响应,并且需要action自行处理response + * + * 日期注解,可以结合@PathVarible,@RequestParam,@RequestHeader一起使用 + * */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target(ElementType.PARAMETER) @Documented -public @interface DownloadResponse { +public @interface DateFormat { + + String value() default "yyyy-MM-dd HH:mm:ss"; + + String timeZone() default "GMT+8"; + } diff --git a/src/main/java/io/jboot/components/restful/annotation/PathVariable.java b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java index 68a962491c0c6af12dfd1c47400f6dfc4d3d1453..4d0c04c4ce581d4d85ebec6d52b40257539a33a8 100644 --- a/src/main/java/io/jboot/components/restful/annotation/PathVariable.java +++ b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java @@ -12,6 +12,9 @@ import java.lang.annotation.*; * float * boolean * long + * bigDecimal + * date + * short */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestBody.java b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java index 73327a66058d243fc484ea12bfc6014c6d7d221e..ae2d882638315234b73cb6a8c588de6c09f681cb 100644 --- a/src/main/java/io/jboot/components/restful/annotation/RequestBody.java +++ b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java @@ -5,13 +5,16 @@ import java.lang.annotation.*; /** * 请求体参数注解 * 支持如下类型参数: - * string / string[] - * int / int[] - * double / double[] - * float / float[] - * boolean / boolean[] - * long / long[] - * object / object[] + * string + * int + * double + * float + * boolean + * long + * object + * bigDecimal + * date + * short */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java index 5a9679701f541fc920d706a69a9368d3546bc821..9a9c361c8e7aee0adf3a94bc374881e8950df4f3 100644 --- a/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java +++ b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java @@ -11,7 +11,9 @@ import java.lang.annotation.*; * float * boolean * long - * + * bigDecimal + * date + * short */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestParam.java b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java index e4a16089e93e9727d943267238e0caf26fbb0a2a..906af5649de62d0167ff47dd0cedb2c6878d0fe3 100644 --- a/src/main/java/io/jboot/components/restful/annotation/RequestParam.java +++ b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java @@ -11,7 +11,9 @@ import java.lang.annotation.*; * float / float[] * boolean / boolean[] * long / long[] - * + * bigDecimal / bigDecimal[] + * date / date[] + * short / short[] */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java deleted file mode 100644 index 081867cccb5c665399abf5116e70affe88ca95b4..0000000000000000000000000000000000000000 --- a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 参数为空错误 - */ -public class ParameterNullErrorException extends RuntimeException { - - private String parameterName; - - - public String getParameterName() { - return parameterName; - } - - public ParameterNullErrorException(String parameterName) { - super("Parameter '"+parameterName+"' specifies a forced check, but the value is null"); - this.parameterName = parameterName; - } - - public ParameterNullErrorException(Exception e) { - super(e); - } - -} diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java deleted file mode 100644 index 2a025ab6563b66edd8b7c875b4ab68d67c4aaf07..0000000000000000000000000000000000000000 --- a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 参数类型错误 - */ -public class ParameterParseErrorException extends RuntimeException { - - - private String parameterValue; - - private String parameterName; - - private Class parameterType; - - public String getParameterValue() { - return parameterValue; - } - - public String getParameterName() { - return parameterName; - } - - public Class getParameterType() { - return parameterType; - } - - public ParameterParseErrorException(String parameterValue, String parameterName, Class parameterType) { - super("Error resolving parameter '" + parameterName + "', unable to match value '" - + parameterValue + "' to specified type '" + parameterType.getName() + "'"); - this.parameterValue = parameterValue; - this.parameterName = parameterName; - this.parameterType = parameterType; - } - - public ParameterParseErrorException(Exception e) { - super(e); - } - -} diff --git a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java b/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java deleted file mode 100644 index e72f83c97ea85862d73754f77e24d9396aa66cef..0000000000000000000000000000000000000000 --- a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 请求方法错误 - */ -public class RequestMethodErrorException extends RuntimeException { - - private String actionKey; - - private String actionMethod; - - private String target; - - private String targetMethod; - - public String getActionKey() { - return actionKey; - } - - public String getActionMethod() { - return actionMethod; - } - - public String getTarget() { - return target; - } - - public String getTargetMethod() { - return targetMethod; - } - - public RequestMethodErrorException(String actionKey, String actionMethod, String target, String targetMethod) { - super("'" + target + "' is specified as a '" + actionMethod + "' request. '" + targetMethod + "' requests are not supported"); - this.actionKey = actionKey; - this.actionMethod = actionMethod; - this.target = target; - this.targetMethod = targetMethod; - } -} diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index 78b01fd1caeed0d8ba62ba81e6d602f94b0e12b0..9c3405585643bee54b9bdbe7fcff568a469fba4d 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -32,15 +32,27 @@ public class JbootrpcConfig { //用于直连时的配置,直连一般只用于测试环境 //com.service.AAAService:127.0.0.1:8080,com.service.XXXService:127.0.0.1:8080 - private Map urls; + private Map urls; //服务的provider指定,可以通过注解 @RPCBean 指定,也可以通过此处指定,此处的配置优先于注解 //com.service.AAAService:providerName,com.service.XXXService:providerName - private Map providers; + private Map providers; //服务的consumer指定,可以通过注解 @RPCInject 指定,也可以通过此处指定,此处的配置优先于注解 //com.service.AAAService:providerName,com.service.XXXService:providerName - private Map consumers; + private Map consumers; + + //当不配置的时候,默认版本号 + private String defaultVersion = "1.0.0"; + + //指定的服务的版本号 + private Map versions; + + //当不指定的时候,默认分组 + private String defaultGroup; + + //指定的服务的分组 + private Map groups; //本地自动暴露 @RPCBean 的 service private boolean autoExportEnable = true; @@ -62,7 +74,7 @@ public class JbootrpcConfig { this.urls = urls; } - public String getUrl(String serviceClass){ + public String getUrl(String serviceClass) { return urls == null ? null : urls.get(serviceClass); } @@ -74,7 +86,7 @@ public class JbootrpcConfig { this.providers = providers; } - public String getProvider(String serviceClass){ + public String getProvider(String serviceClass) { return providers == null ? null : providers.get(serviceClass); } @@ -86,10 +98,52 @@ public class JbootrpcConfig { this.consumers = consumers; } - public String getConsumer(String serviceClass){ + public String getConsumer(String serviceClass) { return consumers == null ? null : consumers.get(serviceClass); } + public String getDefaultVersion() { + return defaultVersion; + } + + public void setDefaultVersion(String defaultVersion) { + this.defaultVersion = defaultVersion; + } + + public Map getVersions() { + return versions; + } + + public void setVersions(Map versions) { + this.versions = versions; + } + + public String getVersion(String className) { + String version = versions == null || versions.isEmpty() ? null : versions.get(className); + return version == null ? defaultVersion : version; + } + + public String getDefaultGroup() { + return defaultGroup; + } + + public void setDefaultGroup(String defaultGroup) { + this.defaultGroup = defaultGroup; + } + + public Map getGroups() { + return groups; + } + + public void setGroups(Map groups) { + this.groups = groups; + } + + public String getGroup(String className) { + String group = groups == null || groups.isEmpty() ? null : groups.get(className); + return group == null ? defaultGroup : group; + } + public boolean isAutoExportEnable() { return autoExportEnable; } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 169e2bec25eaf2ee28d4dc486d0c1784ad39770c..1ce4648f1dcee689db9c47415dcbe7f2b78bccfd 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -41,15 +41,23 @@ public class JbootDubborpc extends JbootrpcBase { reference.setInterface(interfaceClass); String directUrl = rpcConfig.getUrl(interfaceClass.getName()); - if (StrUtil.isNotBlank(directUrl)){ + if (StrUtil.isNotBlank(directUrl)) { reference.setUrl(directUrl); } String consumer = rpcConfig.getConsumer(interfaceClass.getName()); - if (consumer != null){ + if (consumer != null) { reference.setConsumer(DubboUtil.getConsumer(consumer)); } + if (reference.getGroup() == null) { + reference.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (reference.getVersion() == null) { + reference.setVersion(rpcConfig.getVersion(interfaceClass.getName())); + } + return reference.get(); } @@ -61,10 +69,18 @@ public class JbootDubborpc extends JbootrpcBase { service.setRef((T) object); String provider = rpcConfig.getProvider(interfaceClass.getName()); - if (provider != null){ + if (provider != null) { service.setProvider(DubboUtil.getProvider(provider)); } + if (service.getGroup() == null) { + service.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (service.getVersion() == null) { + service.setVersion(rpcConfig.getVersion(interfaceClass.getName())); + } + service.export(); return true; } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index f9c1c4b4863779b5993bb3b53a65e2f0ebd34076..9d4282c63717395449ca986880aebd0a29b6ac0f 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -51,6 +51,14 @@ public class JbootMotanrpc extends JbootrpcBase { referer.setBasicReferer(MotanUtil.getBaseReferer(consumer)); } + if (referer.getGroup() == null) { + referer.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (referer.getVersion() == null) { + referer.setVersion(rpcConfig.getVersion(interfaceClass.getName())); + } + return referer.getRef(); } @@ -62,19 +70,27 @@ public class JbootMotanrpc extends JbootrpcBase { MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, false); - ServiceConfig serviceConfig = MotanUtil.toServiceConfig(config); - serviceConfig.setInterface(interfaceClass); - serviceConfig.setRef((T) object); - serviceConfig.setShareChannel(true); - serviceConfig.setExport(defaultConfig.getExport(interfaceClass.getName())); - serviceConfig.setHost(defaultConfig.getHost(interfaceClass.getName())); + ServiceConfig service = MotanUtil.toServiceConfig(config); + service.setInterface(interfaceClass); + service.setRef((T) object); + service.setShareChannel(true); + service.setExport(defaultConfig.getExport(interfaceClass.getName())); + service.setHost(defaultConfig.getHost(interfaceClass.getName())); String provider = rpcConfig.getProvider(interfaceClass.getName()); if (provider != null) { - serviceConfig.setBasicService(MotanUtil.getBaseService(provider)); + service.setBasicService(MotanUtil.getBaseService(provider)); + } + + if (service.getGroup() == null) { + service.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (service.getVersion() == null) { + service.setVersion(rpcConfig.getVersion(interfaceClass.getName())); } - serviceConfig.export(); + service.export(); MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java index 1f96175f11b28ab8eb92ca906d22a462a1abb401..258c8f1fd74d5907937d8f874566a41b31f55b81 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java @@ -77,7 +77,7 @@ public class MotanrpcConfig { } public String getExport(String className) { - String export = exports == null ? null : exports.get(className); + String export = exports == null || exports.isEmpty() ? null : exports.get(className); return StrUtil.isNotBlank(export) ? export : defaultExport; } @@ -90,7 +90,7 @@ public class MotanrpcConfig { } public String getHost(String className) { - String host = hosts == null ? null : hosts.get(className); + String host = hosts == null || hosts.isEmpty() ? null : hosts.get(className); return StrUtil.isNotBlank(host) ? host : defaultHost; } } diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index d52098103f99521d39b49f4db6d2b936a36a4000..201fa77ac462c29c4f3e6e58020bb007618c2c6d 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -80,7 +80,6 @@ public class JbootCoreConfig extends JFinalConfig { private JbootRestfulManager.Config restfulConfig = new JbootRestfulManager.Config(); - public JbootCoreConfig() { initSystemProperties(); @@ -213,9 +212,9 @@ public class JbootCoreConfig extends JFinalConfig { .setBaseViewPath(routes.getBaseViewPath()) .setMappingSupperClass(routes.getMappingSuperClass()) .setRouteInterceptors(routes.getInterceptors()); - for (Routes.Route route : restfulRoutes) { + restfulRoutes.forEach((Routes.Route route) -> { JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); - } + }); routeList.addAll(restfulRoutes); } @@ -293,11 +292,10 @@ public class JbootCoreConfig extends JFinalConfig { handlers.add(new JbootGatewayHandler()); handlers.add(new JbootFilterHandler()); handlers.add(new JbootHandler()); -// handlers.setActionHandler(new RestfulHandler()); //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { - if (restfulRoutes.isEmpty()) { + if (!restfulRoutes.isEmpty()) { handlers.setActionHandler(new RestfulHandler()); } else { handlers.setActionHandler(new JbootActionHandler()); @@ -326,7 +324,6 @@ public class JbootCoreConfig extends JFinalConfig { JbootGatewayManager.me().init(); JbootRestfulManager.me().init(restfulConfig); - JbootAppListenerManager.me().onStart(); } diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 1fce8cb103a9e5a1a15168c0b67fe49d0d9e1b92..56cb08dd292874694e58ea9466df1588e89a461e 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -83,6 +83,7 @@ public class ClassScanner { excludeJars.add("commons-cli"); excludeJars.add("commons-math"); excludeJars.add("commons-jxpath"); + excludeJars.add("commons-compress"); excludeJars.add("audience-"); excludeJars.add("hessian-"); excludeJars.add("metrics-"); @@ -214,6 +215,10 @@ public class ClassScanner { excludeJars.add("hutool-"); excludeJars.add("jakarta."); excludeJars.add("protostuff-"); + excludeJars.add("poi-"); + excludeJars.add("easypoi-"); + excludeJars.add("ognl-"); + excludeJars.add("xmlbeans-"); } @@ -256,7 +261,7 @@ public class ClassScanner { addUnscanClass("com.rabbitmq."); addUnscanClass("com.squareup."); addUnscanClass("com.typesafe."); - addUnscanClass("com.weibo."); + addUnscanClass("com.weibo.api.motan."); addUnscanClass("com.zaxxer."); addUnscanClass("com.mysql."); addUnscanClass("org.gjt."); diff --git a/src/main/java/io/jboot/components/restful/HttpStatus.java b/src/main/java/io/jboot/web/HttpStatus.java similarity index 95% rename from src/main/java/io/jboot/components/restful/HttpStatus.java rename to src/main/java/io/jboot/web/HttpStatus.java index aa54f020457de13e9349a2bb34549b8e6d2ae8c8..e1e8833cf18f2119bf77f4f76ca421c52bdfe8e4 100644 --- a/src/main/java/io/jboot/components/restful/HttpStatus.java +++ b/src/main/java/io/jboot/web/HttpStatus.java @@ -1,5 +1,24 @@ -package io.jboot.components.restful; +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * 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 + *

+ * 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 io.jboot.web; +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/6 + */ public enum HttpStatus { /** @@ -401,5 +420,4 @@ public enum HttpStatus { public String getReasonPhrase() { return this.reasonPhrase; } - } diff --git a/src/main/java/io/jboot/web/ResponseEntity.java b/src/main/java/io/jboot/web/ResponseEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..1e9153d3797508f3a4bddea00565972bd977e196 --- /dev/null +++ b/src/main/java/io/jboot/web/ResponseEntity.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * 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 + *

+ * 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 io.jboot.web; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/6 + */ +public class ResponseEntity { + + //响应的数据 + private Object body; + + //自定义响应头部信息 + private Map headers; + + //默认http状态 + private HttpStatus httpStatus = HttpStatus.OK; + + + public ResponseEntity() { + } + + public ResponseEntity(Object body) { + this.body = body; + } + + public ResponseEntity(Map headers, HttpStatus httpStatus) { + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity(Object body, Map headers, HttpStatus httpStatus) { + this.body = body; + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity body(Object body) { + this.body = body; + return this; + } + + public ResponseEntity header(String key, String value) { + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.put(key, value); + return this; + } + + public ResponseEntity status(int status) { + for (HttpStatus httpStatus : HttpStatus.values()) { + if (Objects.equals(httpStatus.value(), status)) { + this.httpStatus = httpStatus; + break; + } + } + return this; + } + + + public ResponseEntity status(HttpStatus status) { + this.httpStatus = status; + return this; + } + + + public T getBody() { + return (T) body; + } + + public Map getHeaders() { + return headers; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + + public static ResponseEntity ok() { + ResponseEntity responseEntity = new ResponseEntity(); + return responseEntity.status(HttpStatus.OK); + } + + +} diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index c5a0a4df68cd28b1ba8d49c1a48165c903b9b6e8..114861e8624a1509faf9fa203f57f94b95fb5591 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -120,7 +120,7 @@ public class JbootActionHandler extends ActionHandler { } if (render == null - && invocation.getReturnValue() != null + && void.class != action.getMethod().getReturnType() && renderManager.getRenderFactory() instanceof JbootRenderFactory) { JbootRenderFactory jrf = (JbootRenderFactory) renderManager.getRenderFactory(); render = jrf.getReturnValueRender(action, invocation.getReturnValue()); diff --git a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java new file mode 100644 index 0000000000000000000000000000000000000000..69efd9421bc55c53cc10932273ea435b17939887 --- /dev/null +++ b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * 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 + *

+ * 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 io.jboot.web.render; + +import com.jfinal.kit.JsonKit; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; +import io.jboot.web.ResponseEntity; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/7 + */ +public class JbootResponseEntityRender extends Render { + + private ResponseEntity responseEntity; + + public JbootResponseEntityRender(ResponseEntity responseEntity) { + this.responseEntity = responseEntity; + } + + @Override + public void render() { + + PrintWriter writer = null; + try { + //默认输出 json,但是 responseEntity 可以配置 Header 覆盖这个输出 + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Content-Type", "application/json; charset=utf-8"); + response.setStatus(responseEntity.getHttpStatus().value()); + + Map headers = responseEntity.getHeaders(); + if (headers != null && !headers.isEmpty()) { + for (Map.Entry entry : headers.entrySet()) { + response.setHeader(entry.getKey(), entry.getValue()); + } + } + + String jsonText = responseEntity.getBody() == null ? "" : JsonKit.toJson(responseEntity.getBody()); + writer = response.getWriter(); + writer.write(jsonText); + // writer.flush(); + } catch (IOException e) { + throw new RenderException(e); + } + + } +} diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index caab4d1344aa0d2b2778ffdd3c34151e92bb466c..3e923a00bf014864d403c9e833dc58b005caff44 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -17,10 +17,8 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; -import com.jfinal.render.FileRender; -import com.jfinal.render.JsonRender; -import com.jfinal.render.Render; -import com.jfinal.render.TextRender; +import com.jfinal.render.*; +import io.jboot.web.ResponseEntity; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,19 +40,29 @@ public class JbootReturnValueRender extends Render { private Render render; public JbootReturnValueRender(Action action, Object returnValue) { + this.action = action; - if (isBaseType(returnValue)) { + + if (returnValue == null) { + this.value = null; + } else if (isBaseType(returnValue)) { this.value = String.valueOf(returnValue); } else { this.value = returnValue; } - if (this.value instanceof File) { - this.render = new FileRender((File) value); + if (this.value == null) { + this.render = new NullRender(); + } else if (this.value instanceof ResponseEntity) { + this.render = new JbootResponseEntityRender((ResponseEntity) value); } else if (this.value instanceof String) { this.render = new TextRender((String) value); } else if (this.value instanceof Date) { this.render = new TextRender(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value)); + } else if (this.value instanceof File) { + this.render = new FileRender((File) value); + } else if (this.value instanceof Render) { + this.render = (Render) value; } else { this.render = new JsonRender(JsonKit.toJson(value)); } @@ -80,9 +88,6 @@ public class JbootReturnValueRender extends Render { private boolean isBaseType(Object value) { - if (value == null) { - return true; - } Class c = value.getClass(); return c == String.class || c == char.class || c == Integer.class || c == int.class diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index fcbdf8cd6098c2f020cc815193189a7f0da4cdc3..d402d71831a26d6de7e4890cb7ccb1ad279d725a 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -18,4 +18,11 @@ public class IndexController extends JbootController { public void error500(){ } + + public String ping(){ + return "ping:" + getPara("ping"); + } + + + } diff --git a/src/test/java/io/jboot/test/gateway/GatewayController.java b/src/test/java/io/jboot/test/gateway/GatewayController.java new file mode 100644 index 0000000000000000000000000000000000000000..7cca21d82ae58c51d8061956cdc10f07613a0677 --- /dev/null +++ b/src/test/java/io/jboot/test/gateway/GatewayController.java @@ -0,0 +1,26 @@ +package io.jboot.test.gateway; + +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/5 + */ +@RequestMapping("/gateway") +public class GatewayController extends JbootController { + + public void index(){ + + } + + + public void render(){ + Map map = new HashMap(); + map.putAll(getParas()); + renderJson(map); + } +} diff --git a/src/test/java/io/jboot/test/restful/RestfulController.java b/src/test/java/io/jboot/test/restful/RestfulController.java index 08a2367da89812b5c180b9358db61380e94cad13..c0774faa90733edee299785279558c8ac297e330 100644 --- a/src/test/java/io/jboot/test/restful/RestfulController.java +++ b/src/test/java/io/jboot/test/restful/RestfulController.java @@ -7,15 +7,18 @@ import com.jfinal.aop.Invocation; import com.jfinal.core.NotAction; import com.jfinal.kit.JsonKit; import com.jfinal.kit.StrKit; -import io.jboot.components.restful.HttpStatus; -import io.jboot.components.restful.ResponseEntity; import io.jboot.components.restful.annotation.*; +import io.jboot.web.HttpStatus; +import io.jboot.web.ResponseEntity; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; import io.jboot.web.cors.EnableCORS; import java.io.Serializable; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; @RestController @@ -84,8 +87,10 @@ public class RestfulController extends JbootController { // GET /restful/users @GetMapping("/users") - public ResponseEntity> entityUsers(){ - return new ResponseEntity<>(initData()).addHeader("x-token", StrKit.getRandomUUID()).setHttpStatus(HttpStatus.ACCEPTED); + public ResponseEntity entityUsers(){ + return new ResponseEntity(initData()) + .header("x-token", StrKit.getRandomUUID()) + .status(HttpStatus.ACCEPTED); } // PUT /restful @@ -113,5 +118,23 @@ public class RestfulController extends JbootController { System.out.println("delete by name : " + name); System.out.println("get token header : " + token); } - + + @GetMapping("/get-date") + public Date getDate(){ + return new Date(); + } + + @PutMapping("/input-date") + public void inputDate(@RequestParam @DateFormat Date date){ + System.out.println("input date:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); + renderNull(); + } + + @PutMapping("/input-big-decimal") + public void inputBigDecimal(@RequestParam BigDecimal num){ + System.out.println("input bigDecimal:"+num.toString()); + renderNull(); + } + + } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 151711f485683539297be6b486cb56d94b56486b..f391de5e3c9e2a101ffbb4c30037ffc69a29037a 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -37,16 +37,11 @@ config.test.test.ccc.name = name3 config.test.test.ccc.type = type3 -# 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 +# 配置 gateway ,当访问 /gateway 的时候,自动路由到 /gateway/render jboot.gateway.enable = true -jboot.gateway.uri = https://www.baidu.com -#jboot.gateway.proxyRetries = 2 -jboot.gateway.pathEquals = / - -jboot.gateway.gitee.enable = true -jboot.gateway.gitee.uri= https://gitee.com -jboot.gateway.gitee.sentinelEnable= true -jboot.gateway.gitee.sentinelBlockPage= / -jboot.gateway.gitee.pathStartsWith = /fuhai +jboot.gateway.uri = http://127.0.0.1:9999/gateway/render +jboot.gateway.pathEquals = /gateway + + jboot.rpc.type = local