6. Impement request handler and url mapping

6.1 @RequestHandler

It is not complicated to implement a request handler. @RequestHandler can be used to annotate a handle method in arbitrary Java class which will be treated as a request handler.

Example 6.1. 


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;  
    }  
}  

      

Take a look at the parameters of the handle method, the handle method of a request handler accepts parameter injection as same as the snippet method. More details can be found at the descriptions of the snippet method.


6.2 delcare url rule for request handler

Previously we have introduced how to forward a http request to a certain template file by url mapping. We can declare a request handler for q http request by the same way:

Example 6.2. 


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)  

      

Simply explain:

  1. Forward the request to "/app/handler" to the request handler "LoginHandler".

  2. If "LoginHandler" returns a result of "LoginFailure", forward the request to the template file "error.html".

  3. If "LoginHandler" returns a result of "FurtherConfirm", redirect the current request to "/app/furtherConfirm" by http code 302(302 is by default).

  4. If "LoginHandler" does not return a meaningful result(it usually means success by a null return) , forward the request to the template file "success.html".


More verbose details:

The handler method is used to add a request handler and accepts arbitrary type as the the parameter: an instance of java.lang.Class or an arbitrary instance. The framework explains received parameters by the implementation of DeclareInstanceResolver configured by WebApplicationConfiguration. Thee default implementation provided by framework follows the following rules to explain the declaration of request handler:

  1. If an instance of java.lang.Class is specified, the instance of request handler will be created by invoke "newInstance()" on the specified Class.

  2. If a string is specified, the string will be treated as a class name and an instance of java.lang.Class will be created by calling "Class#forName", then the instance of request handler will be created by invoke "newInstance()" on the created Class.

  3. The specified parameter will be treated as a request handler directly if it is neither a Class nor a string. By this rule, it is interesting that we can declare an anonymous class as a request handler:

    Example 6.3. 

    
    rules.add("/app/handler")  
         .handler(new Object(){  
            @RequestHandler  
            public void handle(){  
                //  
            }  
         }); 
    
                

The asta4d-spring package also provides a resolver based on Spring IOC container, the request handler instance will be retrieved by passing the specified parameter to the "Context#getBean" method of spring container.

The forward method adds a transforming rule for the result of request handler.There must be a handler method annotated by @RequestHandler and the returned value of the handle method is viewed as the result of the request handler, thrown exceptions in the handle method are treated as the result too. The framework will attempt to match the result to the expected result specified by forward method then transforms the result to the corresponding template file(The real mechanism of result transforming is more complicated and is explained at ...). When the matching attempt is performed, the equals method will be used at first, if the equals method returns false and the expected result by forward method is an instance of java.lang.Class, "Class#isAssignableFrom" will be utilized, if false again, skip the current forward rule and do the same check on the next forward rule. A forward rule without the expected result specified will be viewed as a default rule, if matched forward rule for a result is missing or the request handler returns a pointless result(void declaration of handle method or returns null), the default rule will be applied.

The redirect method follows the same rule of forward method except it will cause a 302 redirect(302 is default, 301 can be declared) instead of forwarding to a template file.

6.3 Default request handler

You can not only declare request handler to a certain url, but also declare global request handlers which is available to all the urls and prior to the request handlers declared on certain urls. There is a conception of request handler chain for multi handlers, we will explain it in a later chapter.

Example 6.4. Default request handler


rules.addDefaultRequestHandler(GlobalHandler.class);  
  
rules.addDefaultRequestHandler("authcheck", AuthCheckHandler.class);  

      

At the second line declaration for AuthCheckHandler, an attribute can be specified at the same time, which cause the declared request handler is only available to the rules that declared the same attribute.

Example 6.5. 


rules.add("/app/handler").attribute("authcheck")  
        ...  

      

In the framework's internal implementation, a static match rule table will be generated after all the url mapping declaration finished. A global request handler that is only available to certain rules will be configured to the the certain url rules only, by which unnecessary performance cost is avoid.

In our practice, we found that it is very inconvenient to declare necessary attribute on every rule. In most situations, we will want to do something like configure a same default request handler to all the url paths under "/xxx", which can be done by delcaring a non attribute default request handler which judges the url pattern by itself, but such way will lose the performance benefit of attribute declaration. Thus, we provide an alternative way for this situation: url rule rewrite.

Example 6.6. 


rules.addRuleRewriter(new UrlMappingRuleRewriter() {  
    @Override  
    public void rewrite(UrlMappingRule rule) {  
        if(rule.getSourcePath().startsWith("/privatedata/")){  
            rule.getAttributeList().add("authcheck");  
        }  
    }  
});   

      

Please note that, untill now all the introduced configuration for url mapping are achieved by a group of interface called HandyRule which convert all the declaration to the instance of UrlMappingRule which is used by framework internally. But in the url rule rewritter implementation, developers have to cope with the raw UrlMappingRule which is difficult to understand, so complex url rewriting is not appreciated. Basically, the scenario of url rule rewriting is only for add attributes to rules in bulk.

6.4 Global forward/redirect

Previously we mentioned that the result of a request handler will be matched in the forward declaration, if there is no matched forward found, the global forward rule will be checked.

Example 6.7. Global forward


rules.addGlobalForward(PageNotFoundException.class, "/pagenotfound.html", 404);  

      

The above source declares that if the result of request handler is PageNotFoundException(Exception is treated as the result of the request handler), forward the request to pagenotfound.html by http status code 404.


Global redirect can be declared by the same way.

Example 6.8. Global redirect


rules.addGlobalRedirect(REDIRECT_TO_SOMEWHERE, "/somewhere");  

      

The global forward rules are applied before the default forward rule(the forward rule without result) on certain url mapping rule.

Example 6.9. 


rules.addGlobalForward(PageNotFoundException.class, "/pagenotfound.html", 404);  

rules.addGlobalRedirect(REDIRECT_TO_SOMEWHERE, "/somewhere");  

rules.add("/app/handler")  
     .handler(LoginHandler.class) 
     .forward(LoginFailure.class, "/templates/error.html") 
     .redirect(FurtherConfirm.class, "/app/furtherConfirm")  
     .forward("/templates/success.html");

      

In the above sample, the result of LoginHandler will be matched by the following order:

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