当前位置: 首页 > 工具软件 > jfinal-rest > 使用案例 >

jfinal restful.java_JFinal RESTful

公冶弘壮
2023-12-01

在阅读之前, 先参考

当看完这些之后, 大概应该就能理解, RESTful 的规范, 以及 JFinal 的路由实现方式.

首先, 先看下 JFinal 目前的 RESTful 风格路由实现.

翻开 com.jfinal.core.ActionMapping#getAction 方法

/**

* Support four types of url

* 1: http://abc.com/controllerKey                 ---> 00

* 2: http://abc.com/controllerKey/para            ---> 01

* 3: http://abc.com/controllerKey/method          ---> 10

* 4: http://abc.com/controllerKey/method/para     ---> 11

* The controllerKey can also contains "/"

* Example: http://abc.com/uvw/xyz/method/para

*/

Action getAction(String url, String[] urlPara) {

Action action = mapping.get(url);

if (action != null) {

return action;

}

// --------

int i = url.lastIndexOf(SLASH);

if (i != -1) {

action = mapping.get(url.substring(0, i));

urlPara[0] = url.substring(i + 1);

}

return action;

}

这里有说明, 目前 JFinal 支持的路由格式, 在此之外, JFinal 还提供了一个 Restful 的拦截器, 来达到严格的 RESTful url 风格实现./**

* Invocation 中添加 Method method

*

The standard definition is as follows:

index - GET - A view of all (or a selection of) the records

show - GET - A view of a single record

add - GET - A form to post to create

save - POST - Create a new record

edit - GET - A form to edit a single record

update - PUT - Update a record

delete - DELETE - Delete a record

*

* GET    /user      --->  index

* GET    /user/id    --->  show

* GET    /user/add    --->  add

* POST    /user      --->  save

* GET    /user/edit/id  --->  edit

* PUT    /user/id    --->  update

* DELETE  /user/id    --->  delete

*/

public class Restful implements Interceptor {

// ...

}

当看完这些之后, 进入到正题.

首先, JFinal 基本上已经可以很好的实现 RESTful 风格的 url 了, 但是仍然有不足, 无论是默认的路由实现, 或者是 Restful 拦截器的实现, 仍然无法做到全可控的 RESTful 实现.

Restful 拦截器的实现实际上是匹配链接以及 Request method, 然后进行 forward 到相应的 action 中.DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

就没办法通过 Restful 拦截器来实现了.

那么, 所有问题都抛出来了.

因此, 这里要说的就是, 把 JFinal 的路由机制给改了下, 达到完美兼容 RESTful 链接, 并且保留 JFinal 的默认实现.

先不讨论怎么实现, 其实原理也很简单, 看看代码很容易理解. 这里主要说说使用方式, 以及一些问题.

使用的时候非常简单, 首先在你的 AppConfig.configConstant 中, 设置开启 RESTful 模式.@Override

public void configConstant(Constants constants) {

constants.setRestful(true);

constants.setXxx();

}

当设置了 constants.setRestful(true); 之后, 路由解析就是 RESTful 风格实现的, 默认是 false, 也就是目前 JFinal 自身的路由实现.

接下来就是定义你的 RESTful 链接格式就好, 下面是一个测试案例.package com.jfinal.test.restful.test.ctl;

import com.jfinal.core.ActionKey;

import com.jfinal.core.Controller;

import com.jfinal.plugin.activerecord.Record;

import com.jfinal.restful.Method;

import java.util.ArrayList;

import java.util.Enumeration;

import java.util.List;

/**

* Created by iaceob on 2017/3/17.

*/

public class ZooCtl extends Controller {

private List getAttrs() {

List rets = new ArrayList();

Enumeration ens = super.getAttrNames();

while (ens.hasMoreElements()) {

String key = ens.nextElement();

rets.add(new Record().set(key, super.getAttr(key)));

}

return rets;

}

private Record buildRet(String intro) {

Record ret = new Record();

ret.set("intro", intro)

.set("attrs", this.getAttrs())

.set("paras", super.getParaMap());

return ret;

}

@ActionKey("/zoo")

public void list() {

super.renderJson(this.buildRet("zoo list"));

}

@ActionKey(value = "/zoo", method = Method.POST)

public void addZoo() {

super.renderJson(this.buildRet("add zoo"));

}

@ActionKey("/zoo/:id")

public void getZoo() {

super.renderJson(this.buildRet("get zoo by id"));

}

@ActionKey(value = "/zoo/:id", method = Method.PUT)

public void putZoo() {

super.renderJson(this.buildRet("put zoo by id"));

}

@ActionKey(value = "/zoo/:id", method = Method.PATCH)

public void patchZoo() {

super.renderJson(this.buildRet("patch zoo by id"));

}

@ActionKey(value = "/zoo/:id", method = Method.DELETE)

public void deleteZoo() {

super.renderJson(this.buildRet("delete zoo by id"));

}

@ActionKey("/zoo/:id/animals")

public void animals() {

super.renderJson(this.buildRet("show zoo animals"));

}

@ActionKey("/zoo/:zooId/animal/:animalId")

public void getAnimal() {

super.renderJson(this.buildRet("show zoo animal by id"));

}

@ActionKey("/zoo/:zid/animal2/:aid")

public void getAnimal2() {

super.renderJson(this.buildRet("show zoo animal by id, 2"));

}

}

使用时也是使用 @ActionKey 注解, url 中的值通过 :name 来标识, ActionKey 注解中的另外一个参数就是绑定相应的请求 method.

获取参数也未作改变, 表单传递的参数仍然通过 getPara() 来获取, 使用方式详细见 JFinal 文档.

但是需要注意的是:开启 RESTful 模式后, getPara() 方法按索引取值的方法将失去效果

例如, /zoo/:id/1-3 若这样的请求则会直接返回 404 错误, 因为 RESTful 模式采用的匹配规则较严格(可查看代码实现, 或对该处进行修改令其支持), 另外若是采用 RESTful 风格链接, 应该也不会使用此方式传参.

存放在 url 中的参数标识无法通过 getPara() 取值.

若要获取 url 中指定的参数值, 需要通过 getAttr() 获取, 例如 /zoo/:id 获取这个 id 时, 使用 getAttr("id").

RESTful 模式与非 RESTful 的 ActionKey 是不可共用的.

这个描述可能比较模糊, 但是要表达的意思是, 如果 ActionKey 中的 url 是采用 RESTful 风格写的, 但是却没有开启 RESTful 模式, 就会发生问题, 非 RESTful 模式是不去分析 url 中参数的.

无需再使用 POST GET 拦截器

JFinal 提供了 GET POST 拦截器, 用以指定某个路由只能某中请求, 但是开启 RESTful 模式后, 在路由匹配是就已经限定了指定的 method 访问, 因此无需再使用 `@Before(POST.class)` 这样的代码.

使用方式大抵如此, 剩下还有一些可能会令人不爽的小问题.

使用 getAttr() 获取 url 中参数时, 获取到的值都是 String 类型.

因 HttpServletRequest 自身的设计, 在解析完 url 后无法将值写入到 parameter 中, 因此无法使用 JFinal 提供的 getPara() 取值. 后续也考虑过做一个类型自动识别, 但是最终抛弃了, 在不同的系统中, 参数的类型都不同, 自动识别是不靠谱的, 因此建议使用中, 在获取这个参数时都写一个 validator 对这个值进行验证, controller 中就可以对该值进行强转类型.

性能问题

也不要想得太过与悲观, 这里说的性能只是相对于 JFinal 自身的路由实现. JFinal 的路由实现非常简单,

甚至可以就简单的看成是字符串匹配, 但是这里的 RESTful 模式, 匹配时使用了正则, 因此效能肯定会比 JFinal

自身实现要来的低一些. 后续会做相关方面性能测试.

非插拔式

意思是, 并非提供扩展, 而是通过修改内核实现, 其原因其实看看 JFinal 提供 Restful

拦截器就知道了 = =, 以及 JFinal 的极简的设计缘故. 首先, JFinal 的路由实现, 路由最初加载进来时, 就通过

ActionMapping 隐射到具体的 Action 中, 随后只要有请求就通过 ActionHandler 找到具体的 Action,

因此想要在此基础上实现 RESTful 几乎是不可能, Restful 拦截器的实现也只能通过 forward 来实现, 且无法在 url

中指定参数. 也就是说, 无论你是加自己的过滤器, 甚至创建一个新的注解, 在请求进来时最先经过的 ActionHandler

就以及拒绝了请求, 无法进入到新定义的路由处理中. 因此要实现就只能从 ActionMapping 中入手, 修改路由实现方式.

基本就这样.

@jfinal 官方库收不收这功能, 收我推给你 XDD

------

百度的 ueditor 好难用 = =, 换 markdown 呗, 格式全没了, http://blog.3u3.me/post/jfinal-restful/ 这里有格式.

 类似资料:

相关阅读

相关文章

相关问答