From 34cb4f87b460da7d0dfbe93c53725cc52d52fdc5 Mon Sep 17 00:00:00 2001 From: Redkale <22250530@qq.com> Date: Fri, 25 Nov 2016 15:52:52 +0800 Subject: [PATCH] --- plugins.html | 5 - service.html | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 323 insertions(+), 8 deletions(-) diff --git a/plugins.html b/plugins.html index a514cbde5..0a7c2175d 100644 --- a/plugins.html +++ b/plugins.html @@ -28,11 +28,6 @@

         org.redkalex 是官方插件的根包名。 redkale-plugins 提供了一些常用的插件。

-

REST插件

- -

        org.redkalex.rest - 包提供 RESTful API,便于敏捷开发, 详情点击这里

-

支付插件

        org.redkalex.pay diff --git a/service.html b/service.html index 1ad5a4267..85298b294 100644 --- a/service.html +++ b/service.html @@ -290,11 +290,331 @@

        生成远程模式Service时发现参数带有@RpcCall注解的方法,在远程调用返回结果时会进行回调处理。

Service REST

-

        RestService提供类似Spring Boot的功能。开启REST功能的HTTP Server在实例化标记为@RestService的Service后自动生成对应的HttpServlet,免去开发人员编写HttpServlet的工作量。

+

        RestService提供类似Spring Boot的功能。开启REST功能的HTTP Server在实例化标记为@RestService的Service后自动生成对应的HttpServlet,免去开发人员编写HttpServlet的工作量。RestService生成的HttpServlet均是RestHttpServlet的子类。主要通过@RestService@RestMapping@RestParam这三个注解来实现,同时为了获取其他类型的参数也有@RestAddress@RestCookie@RestHeader@RestSessionidRestOutput提供其扩展功能。

+

+     @RestService :
+

+
/**
+ * 只能依附在Service类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 
+ * (如HelloService/HelloServiceImpl,的默认路径为 hello)。
+ */
+@Target({TYPE})
+public @interface RestService {
+
+    /**
+     * 模块名, 只能是模块名,不能含特殊字符, 只能小写字母+数字,且不能以数字开头
+     *
+     * @return 模块名
+     */
+    String name() default "";
+
+    /**
+     * 模块ID值,鉴权时用到, 对应@WebServlet.moduleid
+     *
+     * @return 模块ID值
+     */
+    int moduleid() default 0;
+
+    /**
+     * 是否屏蔽该类的转换
+     *
+     * @return 默认false
+     */
+    boolean ignore() default false;
+
+    /**
+     * 同@WebServlet.repair
+     *
+     * @return 默认true
+     */
+    boolean repair() default true;
+
+    /**
+     * 备注描述
+     *
+     * @return 备注描述
+     */
+    String comment() default "";
+}
+                
+ +

+     @RestMapping :
+

+
/**
+ * 只能依附在Service实现类的public方法上
+ * value默认为"/" + Service的类名去掉Service及后面字样的小写字符串 (如HelloService,的默认路径为/hello)。
+ */
+@Target({METHOD})
+public @interface RestMapping {
+
+    boolean ignore() default false; //是否屏蔽该方法的转换
+
+    //请求的方法名, 不能含特殊字符
+    //默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX且XXXService为Service的类名将只截取XXX之前)
+    String name() default "";
+
+    String comment() default ""; //备注描述, 对应@WebAction.comment 
+
+    boolean authignore() default true; //是否鉴权,默认不鉴权, 对应@AuthIgnore 
+
+    int cachetimeout() default 0; //结果缓存的秒数, 为0表示不缓存, 对应@HttpCacheable.timeout
+
+    int actionid() default 0; //操作ID值,鉴权时用到, 对应@WebAction.actionid
+
+    String[] methods() default {};  //允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebAction.methods
+
+    String jsvar() default ""; //以application/javascript输出对象是指明js的对象名,该值存在时则忽略contentType()的值
+}
+                
+ +

+     @RestParam :
+

+
/**
+ * 只能依附在Service类的方法的参数上
+ */
+@Target({PARAMETER})
+public @interface RestParam {
+
+    //参数名 name值不能是'&';  name='#'表示截取uri最后一段;  name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值; 
+    //若方法名以find、delete开头且方法的参数只有一个且参数类型是基本数据类型或String,则默认值为"#";
+    //若使用Java 8中带上 -parameters 编译项的新特性,默认值为参数名, 若没使用新特性则采用bean、bean2、bean3...的命名规则。
+    String name(); 
+
+    int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
+
+    String comment() default ""; //备注描述, 对应@WebAction.comment 
+}
+                
+

        开启REST功能的步骤很简单:在 application.xml<server> 节点下增加<rest>指明RestHttpServlet的子类。

+ +
+        <!-- 
+           REST的核心配置项, 存在[rest]节点则Server启动时会加载REST服务, 当Server为SNCP协议时,则SncpServer会变成REST的HttpServer, 节点可以多个
+           base:     REST服务的BaseServlet,必须是 org.redkale.net.http.RestHttpServlet 的子类,该属性值默认值为 org.redkale.net.http.DefaultRestServlet。
+           autoload:默认值"true"  默认值. 加载当前server所能使用的Servce对象;    
+           mustsign:默认值"true" 是否只加载标记为RestService的Service类,默认只加载标记RestService且ignore=false的Service
+           includes:当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
+           excludes:当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
+        -->
+        <rest base="org.redkale.net.http.DefaultRestServlet" mustsign="true" autoload="true" includes="" excludes="">
+            <!-- 
+               value:  Service类名,列出的表示必须被加载的Service对象
+               ignore: 是否忽略,设置为true则不会加载该Service对象,默认值为false
+            -->
+            <service value="com.xxx.XXXXService"/>
+        </rest>
+                
+

        Redkale中的REST并非严格遵循标准的REST规范,在尽量满足规范的同时也考虑到开发的灵活性和简易性。以下通过简单的实例来说明其特性。

+

+         通常配置都需要编写一个 org.redkale.net.http.RestHttpServlet 子类,主要用于获取当前用户信息和鉴权,且必须指定具体的User对象类。开发者的实现类可以参考 redkale-demo 中的BaseServlet类,以下是一个简单的范例:
+

+
public class SimpleRestServlet extends RestHttpServlet<UserInfo> {
+
+    protected static final RetResult RET_UNLOGIN = RetCodes.retResult(RetCodes.RET_USER_UNLOGIN);
+
+    protected static final RetResult RET_AUTHILLEGAL = RetCodes.retResult(RetCodes.RET_USER_AUTH_ILLEGAL);
+
+    @Resource
+    private UserService userService;
+
+    //获取当前用户信息
+    @Override
+    protected UserInfo currentUser(HttpRequest req) throws IOException {
+        String sessionid = req.getSessionid(false);
+        if (sessionid == null || sessionid.isEmpty()) return null;
+        return userService.current(sessionid);
+    }
+
+    //普通鉴权
+    @Override
+    public boolean authenticate(int module, int actionid, HttpRequest request, HttpResponse response) throws IOException {
+        UserInfo info = currentUser(request);
+        if (info == null) {
+            response.finishJson(RET_UNLOGIN);
+            return false;
+        } else if (!info.checkAuth(module, actionid)) {
+            response.finishJson(RET_AUTHILLEGAL);
+            return false;
+        }
+        return true;
+    }
+
+}
+                
+

+         REST的设置方式有两种: 一种采用默认REST注解,一种是显式的设置。
+

+
public class HelloBean implements FilterBean {
+
+    private int helloid;
+
+    @RestHeader(name = "User-Agent")
+    private String useragent; //从Http Header中获取浏览器信息
+    
+    @RestCookie(name = "hello-cookie")
+    private String rescookie;  //从Cookie中获取名为hello-cookie的值
+
+    @RestAddress
+    private String clientaddr;  //客户端请求IP
+
+    @RestSessionid
+    private String sessionid;  //用户Sessionid, 未登录时为null
+
+    /** 以下省略getter setter方法 */
+}
+                
+ +
public class HelloEntity {
+
+    @Id
+    private int helloid;
+
+    private String helloname;
+
+    private int creator;
+
+    private long updatetime;
+
+    private long createtime;
+
+    @RestHeader(name = "hello-res")
+    private String resname;
+
+    @RestAddress
+    private String clientaddr;
+
+    /** 以下省略getter setter方法 */
+}
+                
+ +
/**
+ * 类说明:
+ * Flipper : Source组件中的翻页对象
+ * UserInfo :当前用户类
+ * HelloEntity: Hello模块的实体类
+ * HelloBean: Hello模块实现FilterBean的过滤Bean类
+ *
+ */
+@RestService(name = "hello", moduleid = 0, repair = true, ignore = false, comment = "Hello服务模块")
+public class HelloService implements Service {
+
+    @Resource
+    private DataSource source;
+
+    //增加记录
+    @RestMapping(name = "create", auth = false, comment = "创建Hello对象")
+    public RetResult<HelloEntity> createHello(UserInfo info, @RestParam(name = "bean", comment = "Hello对象") HelloEntity entity) {
+        entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID
+        entity.setCreatetime(System.currentTimeMillis());
+        source.insert(entity);
+        return new RetResult<>(entity);
+    }
+
+    //删除记录
+    @RestMapping(name = "delete", auth = false, comment = "根据id删除Hello对象")
+    public void deleteHello(@RestParam(name = "#", comment = "Hello对象id") int id) { //通过 /hello/delete/1234 删除对象
+        source.delete(HelloEntity.class, id);
+    }
+
+    //修改记录
+    @RestMapping(name = "update", auth = false, comment = "修改Hello对象")
+    public void updateHello(@RestParam(name = "bean", comment = "Hello对象") HelloEntity entity) { //通过 /hello/update?bean={...} 修改对象
+        entity.setUpdatetime(System.currentTimeMillis());
+        source.update(entity);
+    }
+
+    //查询列表
+    @RestMapping(name = "query", auth = false, comment = "查询Hello对象列表")
+    public Sheet<HelloEntity> queryHello(@RestParam(name = "bean", comment = "过滤条件") HelloBean bean, Flipper flipper) { //通过 /hello/query/offset:0/limit:20?bean={...} 查询列表
+        return source.querySheet(HelloEntity.class, flipper, bean);
+    }
+
+    //查询单个
+    @RestMapping(name = "find", auth = false, comment = "根据id查找单个Hello对象")
+    public HelloEntity findHello(@RestParam(name = "#", comment = "Hello对象id") int id) {  //通过 /hello/find/1234 查询对象
+        return source.find(HelloEntity.class, id);
+    }
+}
+                
+ +

+         根据默认命名规则可以看出,以上范例生成的RestServlet与去掉所有@RestService、@RestMapping、@RestParam后的Service生成的是完全相同的。 REST根据Service会动态生成HttpServlet,以上范例自动生成的HttpServlet如下:
+

+ +
@WebServlet(value = {"/hello/*"}, repair = true)
+public class _DynHelloRestServlet extends SimpleRestServlet {
+
+    @Resource
+    private HelloService _service;
+
+    @Resource
+    private Map<String, HelloService> _servicemap;
+
+    @AuthIgnore
+    @WebAction(url = "/hello/create", comment = "创建Hello对象")
+    @WebParam(name = "bean", type = HelloEntity.class, comment = "Hello对象")
+    public void create(HttpRequest req, HttpResponse resp) throws IOException {
+        HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
+        HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean");
+        bean.setClientaddr(req.getRemoteAddr());
+        bean.setResname(req.getHeader("hello-res"));
+        UserInfo user = currentUser(req);
+        RetResult<HelloEntity> result = service.createHello(user, bean);
+        resp.finishJson(result);
+    }
+
+    @AuthIgnore
+    @WebAction(url = "/hello/delete/", comment = "根据id删除Hello对象")
+    @WebParam(name = "#", type = int.class, comment = "Hello对象id")
+    public void delete(HttpRequest req, HttpResponse resp) throws IOException {
+        HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
+        int id = Integer.parseInt(req.getRequstURILastPath());
+        service.deleteHello(id);
+        resp.finishJson(RetResult.success());
+    }
+
+    @AuthIgnore
+    @WebAction(url = "/hello/update", comment = "修改Hello对象")
+    @WebParam(name = "bean", type = HelloEntity.class, comment = "Hello对象")
+    public void update(HttpRequest req, HttpResponse resp) throws IOException {
+        HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
+        HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean");
+        bean.setClientaddr(req.getRemoteAddr());
+        bean.setResname(req.getHeader("hello-res"));
+        service.updateHello(bean);
+        resp.finishJson(RetResult.success());
+    }
+
+    @AuthIgnore
+    @WebAction(url = "/hello/query", comment = "查询Hello对象列表")
+    @WebParam(name = "bean", type = HelloBean.class, comment = "过滤条件")
+    public void query(HttpRequest req, HttpResponse resp) throws IOException {
+        HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
+        HelloBean bean = req.getJsonParameter(HelloBean.class, "bean");
+        bean.setClientaddr(req.getRemoteAddr());
+        bean.setUseragent(req.getHeader("User-Agent"));
+        bean.setRescookie(req.getCookie("hello-cookie"));
+        bean.setSessionid(req.getSessionid(false));
+        Flipper flipper = req.getFlipper();
+        Sheet<HelloEntity> result = service.queryHello(bean, flipper);
+        resp.finishJson(result);
+    }
+
+    @AuthIgnore
+    @WebAction(url = "/hello/find/", comment = "根据id删除Hello对象")
+    @WebParam(name = "#", type = int.class, comment = "Hello对象id")
+    public void find(HttpRequest req, HttpResponse resp) throws IOException {
+        HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
+        int id = Integer.parseInt(req.getRequstURILastPath());
+        HelloEntity bean = service.findHello(id);
+        resp.finishJson(bean);
+    }
+}
+                
-
-