1. @RequestHandler
实现一个request handler并不复杂,任意的java类,用@RequestHandler标记一个handle方法,就实现了一个request handler:
public class LoginHandler {
@RequestHandler
public LoginFailure doLogin(String flag) throws LoginFailure {
if (StringUtils.isEmpty(flag)) {
return null;
}
if ("error".equals(flag)) {
throw new LoginFailure();
}
if (!Boolean.parseBoolean(flag)) {
return new LoginFailure();
}
return null;
}
}
注意方法的参数,和snippet方法一样,request handler的handle方法也接受参数注入,声明方式请参看snippet的说明。
2. 声明Request Handler规则
前面已经介绍过如何配置URL映射将Http请求直接导向模板文件,如果我们需要为特定请求定义一个Request Handler的时候,通过同样的接口配置:
rules.add("/app/handler")
.handler(LoginHandler.class) // (1)
.forward(LoginFailure.class, "/templates/error.html") //(2)
.redirect(FurtherConfirm.class, "/app/furtherConfirm")//(3)
.forward("/templates/success.html"); //(4)
简单的解释一下这段代码:
(1)将“/app/handler”的请求,导向LoginHandler
(2)如果LoginHandler返回的结果是LoginFailure,那么把请求导向模板文件error.html
(3)如果LoginHandler返回的结果FurtherConfirm,那么把请求重定向到“/app/furtherConfirm”
(4)如果LoginHandler没有返回有意义的结果(通常意味着成功),那么把请求导向模板success.html
更详细的说明:
handler方法用来追加一个request handler,参数可以是任意类型:一个Class,一个类实例,或者其他任意数据。框架通过在WebApplicationConfiguration中配置的DeclareInstanceResolver的实现来解释传入的参数,框架内置的缺省resolver的创建逻辑如下:
- 参数如果是Class,则调用newInstance()来创建一个request handler实例
- 如果是String,则将其视为class name,通过Class.forName构建Class后再newInstance创建request handler实例
- 否则将传入的参数直接视为request handler实例。这一点是比较有趣的,因为我们可以在声明URL映射的时候直接用匿名类来声明一个request handler:
rules.add("/app/handler")
.handler(new Object(){
@RequestHandler
public void handle(){
//
}
});
同时,asta4d-spring包提供了一个基于Spring容器管理的Resolver,这里传入的参数会被传递给Spring容器的Context.getBean方法来获取request handler实例。
forward方法用来追加request handler的结果转换规则。一个request handler一定有一个用@RequestHandler标记的handle方法,handle方法的返回值被视为该handler的结果,同时,如果handle方法中抛出异常,这个异常也同样被视为该handler的结果。返回的结果会同forward的第一个参数进行匹配,然后将该结果转换为相应的模板文件地址(实际的转换规则和机制更加复杂,这里暂不赘述)。返回值匹配的时候,首先会调用equals方法进行判定,如果结果为false而第一个参数又是class的情况,则用isAssignableFrom进行再次判定,如果仍然失败,则跳过该forward规则检查下一个。没有定义第一个参数的forward被视为缺省规则,如果一个结果无法找到匹配的forward规则,或者一个request handler没有返回有意义的结果(handle方法声明为void,或者返回null),那么缺省规则将会生效。
redirect方法和forward方法遵循同样的规则,只是最后不是导向模板文件,而是产生一个302(缺省302,可声明为301)重定向。
3. 全局Request Handler
不仅仅是特定的URL可以配置request handler,我们还可以配置全局request handler,一个全局request handler会在所有(或者一组特定的)url映射上生效并先于个别规则声明的request handler执行。这里牵涉到request handler chain的概念,暂不解释,后面详述。
rules.addDefaultRequestHandler(GlobalHandler.class);
rules.addDefaultRequestHandler("authcheck", AuthCheckHandler.class);
需要特别解释一下第二行代码,在声明全局request handler的时候,可以同时为该handler指定一个attribute,这样,该handler只在声明了对应attribute的url映射上有效:
rules.add("/app/handler").attribute("authcheck")
...
※: 框架的内部实现中,URL映射在声明结束后会构建一个静态的规则表,因此只在特定映射上有效的全局handler也只会被构建在特定的映射上,从而避免了不必要的性能代价。
在开发实践中,我们发现,为每一个url映射声明attribute是一件非常麻烦的事情,大多数情况下,我们想做的事情大概就是:对所有“/xxx/”路径下的请求配置同样的request handler。配置无属性的handler然后在handler内部进行判断当然是一个可行的办法,但却失去了通过attribute声明带来的性能上的好处。因此,我们提供了一个替代的解决办法:url rule rewrite:
rules.addRuleRewriter(new UrlMappingRuleRewriter() {
@Override
public void rewrite(UrlMappingRule rule) {
if(rule.getSourcePath().startsWith("/privatedata/")){
rule.getAttributeList().add("authcheck");
}
}
});
需要特别说明一下的是,至今为止介绍的所有的URL映射的配置,都是通过一组称之为HandyRule的接口来完成的,而在HandyRule的内部,所有的设置命令都会被转换成框架内部实际使用的UrlMappingRule接口。而在url rule rewrite的时候,程序员将不得不直接面对比较难于理解的UrlMappingRule接口,因此尽量不要在这里进行复杂的重写逻辑。我们提供这个功能的想定,基本上主要就是批量添加attribute的case。
4. 全局forward/redirect
前面提到,request handler的结果会在映射的forward中进行查找和匹配,如果没有找到匹配的forward规则,那么asta4d会检查是否存在预设的全局forward规则:
rules.addGlobalForward(PageNotFoundException.class, "/pagenotfound.html", 404);
如果request handler的结果是PageNotFoundException(还记得吗,Exception也是结果),那么就导向pagenotfound.html,同时设置返回码404。
同样,可以声明全局的redirect规则:
rules.addGlobalRedirect(REDIRECT_TO_SOMEWHERE, "/somewhere");
全局forward规则的匹配,发生在个别规则定义的无参forward之前,也就是说,就上面举例的LoginHandler的规则而言,结果将会按照下面的顺序匹配:
- LoginFailer
- FurtherConfirm
- PageNotFoundException
- REDIRECT_TO_SOMEWHERE
- (default-> "success.html")
(本来觉得到本节,作为user guide的内容就可以结束了,不过后来想一想,还有一节的内容加进来会更加完整一些。。。)