SpringMVC修炼之旅(3)REST风格与拦截器

一、概述

1.1简介

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

1.2功能

资源:互联网所有的事物都可以被抽象为资源

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。

分别对应 添加、 删除、修改、查询。

1.3优点

  • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
  • 书写简化

1.4RESTful入门案例

java 复制代码
 //设置当前请求方法为DELETE,表示REST风格中的删除操作
    //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
    @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
        System.out.println("user delete..." + id);
        return "{'module':'user delete'}";
    }
bash 复制代码
DELETE http://localhost:8080/users/1

类型:形参注解

位置:SpringMVC控制器方法形参定义前面

作用:用于将URL中的路径变量绑定到方法参数上。这样,当用户访问一个包含路径变量的URL时,这些路径变量的值将被自动传递给相应的方法参数

@RequestBody @RequestParam @PathVariable的区别

区别

  • @RequestParam用于接收url地址传参或表单传参
  • @RequestBody用于接收json数据
  • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数

应用

  • 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
  • 如果发送非json格式数据,选用@RequestParam接收请求参数
  • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值

1.5RESTful快速开发

改进

java 复制代码
package com.yanyu.controller;

import com.yanyu.pojo.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

//@Controller
//@ResponseBody配置在类上可以简化配置,表示设置当前每个方法的返回值都作为响应体
//@ResponseBody
@RestController     //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RequestMapping("/books")
public class BookController {

    //    @RequestMapping( method = RequestMethod.POST)
    @PostMapping        //使用@PostMapping简化Post请求方法对应的映射配置
    public String save(@RequestBody Book book){
        System.out.println("book save..." + book);
        return "{'module':'book save'}";
    }

    //    @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
    @DeleteMapping("/{id}")     //使用@DeleteMapping简化DELETE请求方法对应的映射配置
    public String delete(@PathVariable Integer id){
        System.out.println("book delete..." + id);
        return "{'module':'book delete'}";
    }

    //    @RequestMapping(method = RequestMethod.PUT)
    @PutMapping         //使用@PutMapping简化Put请求方法对应的映射配置
    public String update(@RequestBody Book book){
        System.out.println("book update..."+book);
        return "{'module':'book update'}";
    }

    //    @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
    @GetMapping("/{id}")    //使用@GetMapping简化GET请求方法对应的映射配置
    public String getById(@PathVariable Integer id){
        System.out.println("book getById..."+id);
        return "{'module':'book getById'}";
    }

    //    @RequestMapping(method = RequestMethod.GET)
    @GetMapping             //使用@GetMapping简化GET请求方法对应的映射配置
    public String getAll(){
        System.out.println("book getAll...");
        return "{'module':'book getAll'}";
    }
}

@RestController

类型:类注解

位置:基于SpringMVC的RESTful开发控制器类定义上方

作用:

是Spring框架中的一个注解,用于标识一个类是一个RESTful风格的控制器。它是@Controller和@ResponseBody注解的组合,表示这个类中的方法返回的是数据而不是视图。使用@RestController注解可以简化代码,不需要在每个方法上添加@ResponseBody注解,同时也可以让Spring自动将方法返回的数据转换为JSON格式。这样就可以方便地构建RESTful风格的API。

@GetMapping @PostMapping

@PutMapping @DeleteMapping

类型:方法注解

位置:基于SpringMVC的RESTful开发控制器方法定义上方

作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求

属性 value(默认):请求访问路径

1.6案例实现

实体类

java 复制代码
package com.yanyu.pojo;

import lombok.*;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;


}

控制类

java 复制代码
package com.yanyu.controller;




import com.yanyu.pojo.Book;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/books")
public class BookController {

    @PostMapping
    public String save(@RequestBody Book book){
        System.out.println("book save ==> "+ book);
        return "{'module':'book save success'}";
    }

    @GetMapping
    public List<Book> getAll(){
        System.out.println("book getAll is running ...");
        List<Book> bookList = new ArrayList<Book>();

        Book book1 = new Book();
        book1.setType("计算机");
        book1.setName("SpringMVC入门教程");
        book1.setDescription("小试牛刀");
        bookList.add(book1);

        Book book2 = new Book();
        book2.setType("计算机");
        book2.setName("SpringMVC实战教程");
        book2.setDescription("一代宗师");
        bookList.add(book2);

        Book book3 = new Book();
        book3.setType("计算机丛书");
        book3.setName("SpringMVC实战教程进阶");
        book3.setDescription("一代宗师呕心创作");
        bookList.add(book3);

        return bookList;
    }

}

测试

bash 复制代码
POST http://localhost:8080/books
Content-Type: application/json

{
  "id" : 11111,
  "type" : "计算机丛书",
  "name" : "Springmvc",
  "description" : "好书"

}
bash 复制代码
GET http://localhost:8080/books

配置类-过滤器

java 复制代码
package com.itheima.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //当访问/pages/????时候,从/pages目录下查找内容
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}

二、拦截器

2.1概述

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

**过滤器与拦截器的区别:**拦截器是AOP思想的具体应用。

过滤器

  • servlet规范中的一部分,任何java web工程都可以使用
  • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器

  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的
  1. 浏览器发送一个请求会先到Tomcat的web服务器
  2. Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源
  3. 如果是静态资源,会直接到Tomcat的项目部署目录下去直接访问
  4. 如果是动态资源,就需要交给项目的后台代码进行处理
  5. 在找到具体的方法之前,我们可以去配置过滤器(可以配置多个),按照顺序进行执行
  6. 然后进入到到中央处理器(SpringMVC中的内容),SpringMVC会根据配置的规则进行拦截
  7. 如果满足规则,则进行处理,找到其对应的controller类中的方法进行执行,完成后返回结果
  8. 如果不满足规则,则不进行处理
  9. 这个时候,如果我们需要在每个Controller方法执行的前后添加业务,具体该如何来实现?这个就是拦截器要做的事。

2.2入门案例

拦截器配置

java 复制代码
package com.yanyu.interceptor;



import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;



@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String contentType = request.getHeader("Content-Type");
        HandlerMethod hm = (HandlerMethod)handler;
        System.out.println("preHandle..."+contentType);
        return true;
    }

    @Override
    //原始方法调用后执行的内容
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    @Override
    //原始方法调用完成后执行的内容
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

这段代码是一个基于Spring MVC的拦截器。它实现了HandlerInterceptor接口,并重写了preHandlepostHandleafterCompletion三个方法。在preHandle方法中,首先获取请求头中的"Content-Type",然后将其与处理器对象进行类型转换,最后输出"preHandle..."。postHandle方法中只输出"postHandle..."。afterCompletion方法中只输出"afterCompletion..."。这个拦截器的作用是在请求处理过程中,对请求进行预处理、后处理和完成处理。

监听器

java 复制代码
package com.yanyu.config;


import com.yanyu.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //当访问/pages/????时候,从/pages目录下查找内容
        registry.addResourceHandler("/pages/**")
                .addResourceLocations("/pages/");
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

测试

java 复制代码
POST http://localhost:8080/books
Content-Type: application/json

{
  "id" : 11111,
  "type" : "计算机丛书",
  "name" : "Springmvc",
  "description" : "好书"

}

2.3运行流程

2.4拦截器参数

前置处理方法

原始方法之前运行preHandle

java 复制代码
public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) throws Exception {
    //handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装
    return true;
}

使用handler参数,可以获取方法的相关信息,例如

java 复制代码
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod hm = (HandlerMethod)handler;
    String methodName = hm.getMethod().getName();//可以获取方法的名称
    return true;
}
后置处理方法
java 复制代码
@Override
    //原始方法调用后执行的内容
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

前三个参数和上面的是一致的。
modelAndView: 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
因为咱们现在都是返回 json 数据,所以该参数的使用率不高。

完成处理方法

拦截器最后执行的方法,无论原始方法是否执行

java 复制代码
public void afterCompletion(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            Exception ex) throws Exception {
    System.out.println("afterCompletion");
}
//ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理  
//因为我们现在已经有全局异常处理器类,所以该参数的使用率也不高。

2.5拦截器链配置

拦截器执行的顺序是和配置顺序有关。就和前面所提到的运维人员进入机房的案例,先进后出。
当配置多个拦截器时,形成拦截器链
拦截器链的运行顺序参照拦截器添加顺序为准
当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
当拦截器运行中断,仅运行配置在前面的拦截器的 afterCompletion 操作

相关推荐
hui函数2 小时前
Flask电影投票系统全解析
后端·python·flask
好学且牛逼的马3 小时前
GOLANG 接口
开发语言·golang
ahauedu3 小时前
AI资深 Java 研发专家系统解析Java 中常见的 Queue实现类
java·开发语言·中间件
韭菜钟3 小时前
在Qt中用cmake实现类似pri文件的功能
开发语言·qt·系统架构
闲人编程3 小时前
Python第三方库IPFS-API使用详解:构建去中心化应用的完整指南
开发语言·python·去中心化·内存·寻址·存储·ipfs
计算机编程小咖4 小时前
《基于大数据的农产品交易数据分析与可视化系统》选题不当,毕业答辩可能直接挂科
java·大数据·hadoop·python·数据挖掘·数据分析·spark
CTRA王大大4 小时前
【golang】制作linux环境+golang的Dockerfile | 如何下载golang镜像源
linux·开发语言·docker·golang
zhangfeng11335 小时前
以下是基于图论的归一化切割(Normalized Cut)图像分割工具的完整实现,结合Tkinter界面设计及Python代码示
开发语言·python·图论
flashlight_hi6 小时前
LeetCode 分类刷题:2529. 正整数和负整数的最大计数
python·算法·leetcode
Ashlee_code6 小时前
香港券商櫃台系統跨境金融研究
java·python·科技·金融·架构·系统架构·区块链