Spring基础知识(18)- Spring MVC (八) | 拦截器(Interceptor)、REST 风格


1. 拦截器(Interceptor)

    在系统中,经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的 “权限检测” 及 “日志记录”。当然不仅仅这些,所以需要一种机制,拦截用户的请求,在请求的前后添加处理逻辑。

    Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理。

    Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

    1) 拦截器的定义

        在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,主要有以下 2 种方式。

            (1) 通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义;
            (2) 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。

        本节以实现 HandlerInterceptor 接口的定义方式为例讲解自定义拦截器的使用方法。示例代码如下。

复制代码
 1             package com.example.interceptor;
 2 
 3             import javax.servlet.http.HttpServletRequest;
 4             import javax.servlet.http.HttpServletResponse;
 5             import org.springframework.web.servlet.HandlerInterceptor;
 6             import org.springframework.web.servlet.ModelAndView;
 7 
 8             public class TestInterceptor implements HandlerInterceptor {
 9 
10                 @Override
11                 public boolean preHandle(HttpServletRequest request,
12                         HttpServletResponse response, Object handler) throws Exception {
13                     // preHandle() 方法在控制器的处理请求方法调用之前执行
14                     System.out.println("TestInterceptor -> preHandle()");
15                     return false;
16                 }
17 
18                 @Override
19                 public void postHandle(HttpServletRequest request,
20                         HttpServletResponse response, Object handler,
21                         ModelAndView modelAndView) throws Exception {
22                     // postHandle() 方法在控制器的处理请求方法调用之后,解析视图之前执行
23                     System.out.println("TestInterceptor -> postHandle()");
24                 }
25 
26                 @Override
27                 public void afterCompletion(HttpServletRequest request,
28                         HttpServletResponse response, Object handler, Exception e)
29                         throws Exception {
30                     // afterCompletion() 方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行
31                     System.out.println("TestInterceptor -> afterCompletion()");
32                 }
33             }
复制代码


        上述拦截器的定义中实现了 HandlerInterceptor 接口,并实现了接口中的 3 个方法,说明如下。

            (1) preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作;
            (2) postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改;
            (3) afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作;

    2) 拦截器的配置

        让自定义的拦截器生效需要在 Spring MVC 的配置文件中进行配置,配置示例代码如下:

复制代码
 1             <!-- 配置拦截器 -->
 2             <mvc:interceptors>
 3                 <!-- 配置一个全局拦截器,拦截所有请求 -->
 4                 <bean class="com.example.interceptor.TestInterceptor" />
 5 
 6                 <mvc:interceptor>
 7                     <!-- 配置拦截器作用的路径 -->
 8                     <mvc:mapping path="/**" />
 9                     <!-- 配置不需要拦截作用的路径 -->
10                     <mvc:exclude-mapping path="" />
11 
12                     <!-- 定义 <mvc:interceptor> 元素中,表示匹配指定路径的请求才进行拦截 -->
13                     <bean class="com.example.interceptor.Interceptor1" />
14                 </mvc:interceptor>
15 
16                 <mvc:interceptor>
17                     <!-- 配置拦截器作用的路径 -->
18                     <mvc:mapping path="/test/interceptor" />
19 
20                     <!-- 定义在 <mvc:interceptor> 元素中,表示匹配指定路径的请求才进行拦截 -->
21                     <bean class="com.example.interceptor.Interceptor2" />
22                 </mvc:interceptor>
23             </mvc:interceptors>
复制代码


        在上述示例代码中,元素说明如下。

            <mvc:interceptors>:该元素用于配置一组拦截器。
            <bean>:该元素是 <mvc:interceptors> 的子元素,用于定义全局拦截器,即拦截所有的请求。
            <mvc:interceptor>:该元素用于定义指定路径的拦截器。
            <mvc:mapping>:该元素是 <mvc:interceptor> 的子元素,用于配置拦截器作用的路径,该路径在其属性 path 中定义。path 的属性值为/**时,表示拦截所有路径,值为/gotoTest时,表示拦截所有以/gotoTest结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置。

        需要注意的是,<mvc:interceptor> 元素的子元素必须按照 <mvc:mapping.../>、<mvc:exclude-mapping.../>、<bean.../> 的顺序配置。   

    示例

        下面通过拦截器来完成一个用户登录权限验证的 Web 应用。

        在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。

        (1) 创建 src/main/java/com/example/entity/User.java 文件

复制代码
 1             package com.example.entity;
 2 
 3             public class User {
 4                 private int id;
 5                 private String username;
 6                 private String password;
 7 
 8                 public User() {
 9 
10                 }
11 
12                 public int getId() {
13                     return id;
14                 }
15 
16                 public void setId(int id) {
17                     this.id = id;
18                 }
19 
20                 public String getUsername() {
21                     return this.username;
22                 }
23 
24                 public void setUsername(String username) {
25                     this.username = username;
26                 }
27 
28                 public String getPassword() {
29                     return password;
30                 }
31 
32                 public void setPassword(String password) {
33                     this.password = password;
34                 }
35             }
复制代码


        (2) View
            
            创建 src/main/webapp/WEB-INF/jsp/auth.jsp 文件

复制代码
 1                 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 2                 <html>
 3                 <head>
 4                     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 5                     <title>Auth</title>
 6                 </head>
 7                 <body>
 8                     <h4>Auth Page</h4>
 9                     <p>Message: ${message}</p>
10                     <p>&nbsp;</p>
11 
12                     <form action="${pageContext.request.contextPath }/user/auth/post" method="POST">
13                         <p>Username: <input type="text" name="username" /></p>
14                         <p>Password: <input type="password" name="password" /></p>
15                         <p><input type="submit" value="Submit" /></p>
16                     </form>
17                 </body>
18                 </html>
复制代码


            创建 src/main/webapp/WEB-INF/jsp/main.jsp 文件

复制代码
 1                 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 2                 <html>
 3                 <head>
 4                     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 5                     <title>Main</title>
 6                 </head>
 7                 <body>
 8                     <p>Welcome ${user.username }</p>
 9                     <p><a href="${pageContext.request.contextPath }/user/logout">Exit</a></p>
10                 </body>
11                 </html>
复制代码


        (3) 创建 src/main/java/com/example/interceptor/LoginInterceptor.java

复制代码
 1             package com.example.interceptor;
 2 
 3             import javax.servlet.http.HttpServletRequest;
 4             import javax.servlet.http.HttpServletResponse;
 5             import javax.servlet.http.HttpSession;
 6             import org.springframework.web.servlet.HandlerInterceptor;
 7             import org.springframework.web.servlet.ModelAndView;
 8 
 9             public class LoginInterceptor implements HandlerInterceptor {
10 
11                 @Override
12                 public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
13                                         Object handler) throws Exception {
14                     System.out.println("TestInterceptor -> preHandle()");
15 
16                     // URL
17                     String url = request.getRequestURI();
18                     if (url.indexOf("/user/auth") >= 0 || url.indexOf("/user/auth/post") >= 0) {
19                         return true;
20                     }
21 
22                     // Session
23                     HttpSession session = request.getSession();
24                     Object obj = session.getAttribute("user");
25                     if (obj != null)
26                         return true;
27 
28                     // Check auth
29                     request.setAttribute("message", "Not authorized");
30                     request.getRequestDispatcher("/user/auth").forward(request, response);
31 
32                     return false;
33                 }
34 
35                 @Override
36                 public void postHandle(HttpServletRequest request,
37                                     HttpServletResponse response, Object handler,
38                                     ModelAndView modelAndView) throws Exception {
39                     System.out.println("TestInterceptor -> postHandle()");
40                 }
41 
42                 @Override
43                 public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
44                                             Object object, Exception e) throws Exception {
45                     System.out.println("TestInterceptor -> afterCompletion()");
46                 }
47 
48             }
复制代码


        (4) 修改 springmvc-beans.xml 文件,添加如下配置

1             <!-- 配置拦截器 -->
2             <mvc:interceptors>
3                 <mvc:interceptor>
4                     <!-- 配置拦截器作用的路径 -->
5                     <mvc:mapping path="/**" />
6                     <bean class="com.example.interceptor.LoginInterceptor" />
7                 </mvc:interceptor>
8             </mvc:interceptors>


        (5) 创建 src/main/java/com/example/controller/UserController.java 文件

复制代码
 1             package com.example.controller;
 2 
 3             import javax.servlet.http.HttpSession;
 4             import org.springframework.stereotype.Controller;
 5             import org.springframework.ui.Model;
 6             import org.springframework.web.bind.annotation.RequestMapping;
 7             import com.example.entity.User;
 8 
 9             @Controller
10             @RequestMapping("/user")
11             public class UserController {
12 
13                 @RequestMapping("/auth")
14                 public String auth() {
15                     return "auth";
16                 }
17 
18                 @RequestMapping("/auth/post")
19                 public String authPost(User user, Model model, HttpSession session) {
20                     if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
21                         session.setAttribute("user", user);
22                         return "redirect:/user/main";
23                     }
24                     model.addAttribute("message", "Invalid username or password");
25                     return "auth";
26                 }
27 
28                 @RequestMapping("/main")
29                 public String main() {
30                     return "main";
31                 }
32 
33                 @RequestMapping("/logout")
34                 public String logout(HttpSession session) {
35                     session.invalidate();
36                     return "auth";
37                 }
38             }
复制代码


        访问:http://localhost:9090/user/main


2. REST 风格

    REST(Representational State Transfer)即表述性转移,是目前最流行的一种软件架构风格。它结构清晰、易于理解、有较好的扩展性。

    Spring REST 风格可以简单理解为:使用 URL 表示资源时,每个资源都用一个独一无二的 URL 来表示,并使用 HTTP 方法表示操作,即准确描述服务器对资源的处理动作(GET、POST、PUT、DELETE),实现资源的增删改查。

        GET:表示获取资源
        POST:表示新建资源
        PUT:表示更新资源
        DELETE:表示删除资源

    下面举例说明 REST 风格的 URL 与传统 URL 的区别。

        /user/view.jsp?id=3      VS     /user/view/3
        /user/delete.jsp?id=3    VS     /user/delete/3
        /user/modify.jsp?id=3    VS     /user/modify/3
        
    REST 风格的 URL 中最明显的就是参数不再使用 “?” 传递。这种风格的 URL 可读性更好,使得项目架构清晰,最关键的是 Spring MVC 也提供对这种风格的支持。

    REST 风格在开发 API(不需要view)返回 JSON 或 XML 的接口中,一般使用 @RestController (或 &Controller + @ResponseBody),@RestController (或 &Controller + @ResponseBody) 支持 GET、POST、PUT、DELETE 等动作。

    在处理 Form 表单方式的开发中,由于 Form 的 method 默认不支持 PUT 和 DELETE 请求,所以需要将 DELETE 和 PUT 请求转换成 POST 请求,在 web.xml 中配置过滤器 HiddenHttpMethodFilter。

    本文只讨论 Form 表单方式的 REST 风格, @RestController (或 &Controller + @ResponseBody) 可以参考 “Spring基础知识(17)- Spring MVC (七)” 的 “JSON 数据交互”。

    示例

        在 “Spring基础知识(12)- Spring MVC (二)” 的示例里,更新过 springmvc-beans.xml 的 SpringmvcBasic 项目基础上,修改如下。

        (1) 创建 src/main/webapp/WEB-INF/jsp/rest.jsp 文件

复制代码
 1             <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 2             <html>
 3             <head>
 4                 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 5                 <title> REST Style</title>
 6             </head>
 7             <body>
 8                 
 9                 <h4>发送 GET 请求</h4>
10                 <p><a href="/user/rest/1">GET</a></p>
11                 
12                 <hr />
13                 <h4>发送 POST 请求</h4>
14                 <form action="/user/rest/1" method="POST">
15                     <p><input type="submit" value="POST" /></p>
16                 </form>
17 
18                 <hr />       
19                 <h4>发送 PUT 请求</h4>
20                 <form action="/user/rest/1" method="POST">
21                     <input type="hidden" name="_method" value="PUT" />
22                     <p><input type="submit" value="PUT" /></p>
23                 </form>
24 
25                 <hr />
26                 <h4>发送 DELETE 请求</h4>
27                 <form action="/user/rest/1" method="POST">
28                     <input type="hidden" name="_method" value="DELETE" />
29                     </p><input type="submit" value="DELETE" /></p>
30                 </form>
31                 
32             </body>
33             </html>
复制代码


        (2) 在 web.xml 文件中添加以下代码

复制代码
1              <!-- 将 POST 请求转化为 PUT 请求和 DELETE 请求 -->
2             <filter>
3                 <filter-name>hiddenHttpMethodFilter</filter-name>
4                 <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
5             </filter>
6             <filter-mapping>
7                 <filter-name>hiddenHttpMethodFilter</filter-name>
8                 <url-pattern>/*</url-pattern>
9             </filter-mapping>
复制代码


        (3) 创建 src/main/java/com/example/controller/UserController.java 文件

复制代码
 1             package com.example.controller;
 2 
 3             import org.springframework.ui.Model;
 4             import org.springframework.stereotype.Controller;
 5             import org.springframework.web.bind.annotation.PathVariable;
 6             import org.springframework.web.bind.annotation.RequestMapping;
 7             import org.springframework.web.bind.annotation.RequestMethod;
 8 
 9             @Controller
10             @RequestMapping("/user")
11             public class UserController {
12                 @RequestMapping("/rest")
13                 public String rest() {
14                     return "rest";
15                 }
16                 
17                 @RequestMapping(value = "/rest/{id}", method = RequestMethod.GET)
18                 public String restGet(@PathVariable Integer id, Model model) {
19                     System.out.println("UserController -> restGet(): id = " + id);
20                     model.addAttribute("message", "GET " + id);
21                     return "success";
22                 }
23                 
24                 @RequestMapping(value = "/rest/{id}", method = RequestMethod.POST)
25                 public String restPost(@PathVariable Integer id, Model model) {
26                     System.out.println("UserController -> restPost(): id = " + id);
27                     model.addAttribute("message", "POST " + id);
28                     return "success";
29                 }
30                 
31                 @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
32                 public String restPut(@PathVariable Integer id, Model model) {
33                     System.out.println("UserController -> restPut(): id = " + id);
34                     model.addAttribute("message", "PUT " + id);
35                     return "success";
36                 }
37 
38                 @RequestMapping(value = "/rest/{id}", method = RequestMethod.DELETE)
39                 public String restDelete(@PathVariable Integer id, Model model) {
40                     System.out.println("UserController -> restDelete(): id = " + id);
41                     model.addAttribute("message", "DELETE " + id);
42                     return "success";
43                 }
44 
45             }
复制代码


        访问:http://localhost:9090/user/rest



posted @   垄山小站  阅读(187)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示