play框架05--控制层--action、拦截器

5.4Action链

  Play中的Action链与Servlet API中的forward不尽相同。Play的每次HTTP请求只能调用一个Action,如果需要调用其他的Action,那么必须将浏览器重定向到相应的URL。在这种情况下,浏览器的URL始终与正在执行的Action保持对应关系,使得后退、前进、刷新操作更加清晰。

      调用控制器中其他Action方法也可以实现重定向,框架会拦截该调用并生成正确的HTTP重定向。具体实现如下:

public class Clients extends Controller {
 
   
public static void show(Long id) {
       
Client client = Client.findById(id);
        render
(client);
   
}
 
   
public static void create(String name) {
       
Client client = new Client(name);
        client
.save();
        show
(client.id);
   
}
}

      相应的路由规则定义如下:

GET            /clients/{id}                              Clients.show
POST          
/clients                                   Clients.create

      按照定义,Action链的生命周期为:

  • 浏览器向/clients发送POST请求;
  • 路由器调用Clients控制器中的create方法;
  • create方法直接访问show方法;
  • Java调用被拦截,路由器逆向生成带有id参数的URL来调用Clients.show;
  • HTTP响应重定向为:/clients/3132;
  • 浏览器地址栏中URL展现为:/clients/3132;


5.5、拦截器


    控制器中可以定义拦截方法(也可称之为拦截器),为控制器及其子类的所有Action提供服务。当所有的Action都需要进行通用的处理时,该功能就显得非常有用:比如验证用户的合法性,加载请求范围内的信息等。

      读者在使用时需要注意的是,这些拦截器方法不能定义为public,但必须是static,并通过有效的拦截标记进行注解。


5.5.1 @Before#

      使用@Before注解的方法会在每个Action调用之前执行。如创建具有用户合法性检查的拦截器:

public class Admin extends Application {
 
   
@Before
   
static void checkAuthentification() {
       
if(session.get("user") == null) login();
   
}
 
   
public static void index() {
       
List<User> users = User.findAll();
        render
(users);
   
}
 
   
...
 
}

      如果不希望@Before注解拦截所有的Action方法,那么可以使用unless参数列出需要排除的方法:

public class Admin extends Application {
 
   
@Before(unless="login")
   
static void checkAuthentification() {
       
if(session.get("user") == null) login();
   
}
 
   
public static void index() {
       
List<User> users = User.findAll();
        render
(users);
   
}
 
   
...
 
}

      或者直接使用only参数把需要拦截的方法列举出来:

public class Admin extends Application {
 
   
@Before(only={"login","logout"})
   
static void doSomething() {  
       
...
   
}
   
...
}

      unless和only参数对@After,@Before以及@Finally注解都适用。


5.5.2 @After#

      使用@After注解的方法会在每个Action调用之后执行:

public class Admin extends Application {
 
   
@After
   
static void log() {
       
Logger.info("Action executed ...");
   
}
 
   
public static void index() {
       
List<User> users = User.findAll();
        render
(users);
   
}
 
   
...
 
}


5.5.3 @Catch#

      如果有Action方法抛出了异常,那么使用@Catch注解的方法就会执行,且抛出的异常会以参数的形式传递到@Catch注解的方法中。具体实现如下:

public class Admin extends Application {
       
   
@Catch(IllegalStateException.class)
   
public static void logIllegalState(Throwable throwable) {
       
Logger.error("Illegal state %s…", throwable);
   
}
   
   
public static void index() {
       
List<User> users = User.findAll();
       
if (users.size() == 0) {
           
throw new IllegalStateException("Invalid database - 0 users");
       
}
        render
(users);
   
}
}

      使用@Catch注解和普通的Java异常处理程序一样,捕获父类往往可以获得更多的异常类型。如果拥有多个需要捕获的方法,可以通过指定优先级来确定他们的执行顺序。具体实现如下:

public class Admin extends Application {
 
   
@Catch(value = Throwable.class, priority = 1)
   
public static void logThrowable(Throwable throwable) {
       
// Custom error logging…
       
Logger.error("EXCEPTION %s", throwable);
   
}
 
   
@Catch(value = IllegalStateException.class, priority = 2)
   
public static void logIllegalState(Throwable throwable) {
       
Logger.error("Illegal state %s…", throwable);
   
}
 
   
public static void index() {
       
List<User> users = User.findAll();
       
if(users.size() == 0) {
           
throw new IllegalStateException("Invalid database - 0 users");
       
}
        render
(users);
   
}
}


5.5.4 @Finally#

      @Finally注解的方法总是在每个Action调用之后执行(无论Action是否成功执行):

public class Admin extends Application {
 
   
@Finally
   
static void log() {
       
Logger.info("Response contains : " + response.out);
   
}
 
   
public static void index() {
       
List<User> users = User.findAll();
        render
(users);
   
}
 
   
...
 
}

      如果@Finally注解的方法中包含的参数是可抛出的异常,其方法中的内容还是可以继续执行的,具体如下:

public class Admin extends Application {
 
   
@Finally
   
static void log(Throwable e) {
       
if( e == null ){
           
Logger.info("action call was successful");
       
} else{
           
Logger.info("action call failed", e);
       
}
   
}
 
   
public static void index() {
       
List<User> users = User.findAll();
        render
(users);
   
}
   
...
}


5.5.5 使用@with注解增加更多拦截器#

      如果某个控制器是其他一些类的父类,那么该控制器中定义的所有拦截器会影响到所有子类。由于Java不允许多重继承,对单纯通过继承来使用拦截器造成了一定的局限性。Play可以通过@With注解,调用其他控制器中已经定义好的拦截方法,从而突破这一局限。比如创建Secure控制器,定义checkAuthenticated()拦截方法验证用户合法性:

public class Secure extends Controller {
   
   
@Before
   
static void checkAuthenticated() {
       
if(!session.containsKey("user")) {
            unAuthorized
();
       
}
   
}
}    

      在其他的控制器中,可以通过@With(Secure.class)注解将其包含进来:

@With(Secure.class)
public class Admin extends Application {
   
   
...
}


5.6、Session和Flash作用域


在Play开发中,如果需要在HTTP请求之间保存数据,可以将数据保存在Session或者Flash内。保存在Session中的数据在整个用户会话中都是有效的,而保存在Flash的数据只对下一次请求有效。

      特别需要注意的是,Session和Flash作用域中的数据都是采用Cookie机制添加到随后的HTTP响应中的(并没有存储在服务器上的),所以数据大小非常有限(不能超过4K),而且只能存储字符串类型的数据。

      由于Cookie是使用密钥签名过的,所以客户端不能轻易修改Cookie的数据(否则会失效)。不要将Play的Session当作缓存来使用,如果需要在特定的会话中缓存一些数据,那么可以使用Play内置的缓存机制,并将session.getId()作为缓存的key进行储存。

public static void index() {
   
List messages = Cache.get(session.getId() + "-messages", List.class);
   
if(messages == null) {    
       
// 处理缓存失效
        messages
= Message.findByUser(session.get("user"));
       
Cache.set(session.getId() + "-messages", messages, "30mn");
   
}
    render
(messages);
}

      Session在用户关闭浏览器后就会失效,除非修改配置文件中的application.session.maxAge属性。设置方法如下:

application.session.maxAge=7d  # Remember for one week.

      使用Play内置的Cache缓存时需要注意,Cache与传统Servlet的HTTP Session对象是不同的。框架无法保证这些缓存对象会一直存在,所以在业务代码中必须处理缓存失效的问题,以便保持应用完全无状态化。


猜你喜欢

转载自blog.csdn.net/J_M_S_H_T/article/details/80833441