`
xzer
  • 浏览: 16659 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

友好的开发框架-Asta4D(7):Request Handler的实现与URL映射

阅读更多

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的创建逻辑如下:

  1. 参数如果是Class,则调用newInstance()来创建一个request handler实例
  2. 如果是String,则将其视为class name,通过Class.forName构建Class后再newInstance创建request handler实例
  3. 否则将传入的参数直接视为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的规则而言,结果将会按照下面的顺序匹配:

 

  1. LoginFailer
  2. FurtherConfirm
  3. PageNotFoundException
  4. REDIRECT_TO_SOMEWHERE
  5. (default-> "success.html")

 

 

 (本来觉得到本节,作为user guide的内容就可以结束了,不过后来想一想,还有一节的内容加进来会更加完整一些。。。)

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics