当前位置: 首页 > news >正文

SpringMVC学习(二)——RESTful API、拦截器、异常处理、数据类型转换

一、RESTful 

(一)RESTful概述

        RESTful是一种软件架构风格,用于设计网络应用程序。REST是“Representational State Transfer”的缩写,中文意思是“表现层状态转移”。它基于客户端-服务器模型和无状态操作,以及使用HTTP请求来处理数据。RESTful架构风格强调利用HTTP协议的四个主要方法(GET、POST、PUT、DELETE)来实现资源的访问和操作。以下是RESTful架构的一些核心原则:

  1. 客户端-服务器分离:客户端和服务器之间的交互应该是简单的,服务器端负责存储数据和业务逻辑,客户端负责展示。

  2. 无状态:每个请求从客户端到服务器必须包含所有必要的信息,以便服务器能够理解请求并独立地处理它,不依赖于之前的任何请求。

  3. 可缓存:数据被标记为可缓存或不可缓存。如果数据被标记为可缓存,那么客户端可以缓存数据以提高效率。

  4. 统一接口:系统组件之间的交互通过统一的接口进行,这简化了整体系统架构,使得系统更容易理解、开发和使用。

  5. 分层系统:客户端不能直接了解它所消费的服务之外的任何服务器信息,也不应该知道它的数据是来自一个服务器还是多个服务器。

  6. 按需代码(可选):服务器可以按需向客户端发送代码,比如JavaScript,以便在客户端执行。

        在RESTful架构中,资源(Resources)是核心概念,每个资源都有一个唯一的标识符,通常是一个URI。客户端通过HTTP方法对这些资源进行操作:

  • GET:请求从服务器检索特定资源。GET请求应该是安全的,不会产生副作用。

  • POST:向服务器提交新的资源,通常会导致创建新的资源。

  • PUT:更新服务器上的现有资源。

  • DELETE:从服务器上删除资源。

        RESTful API设计简洁、直观,易于理解和使用,因此在现代网络应用中非常流行

代码示例: 

@CrossOrigin  // 允许跨域请求
@RestController
@RequestMapping("/emp")
public class EmpController {@Autowiredprivate EmpService empService;/*** 查询员工*/@GetMapping("/{empno}")public R queryEmpById(@PathVariable("empno") Integer empno) {Emp emp = empService.queryById(empno);return R.ok(emp);}/*** 新增员工*/@PostMappingpublic R addEmp(@RequestBody Emp emp) {empService.save(emp);return R.ok();}/*** 修改员工*/@PutMappingpublic R editEmp(@RequestBody Emp emp) {empService.update(emp);return R.ok();}/*** 删除员工*/@DeleteMapping("/{empno}")public R deleteEmpById(@PathVariable("empno") Integer empno) {empService.deleteById(empno);return R.ok();}/*** 查询所有员工*/@GetMapping("/getAll")public R queryEmpList() {List<Emp> empList = empService.getList();return R.ok(empList);}
}

(二)@PathVariable:从URL中提取路径变量

        @PathVariable是Spring MVC中用于从URL中提取路径变量的注解。它允许将URL模板中的占位符映射到方法参数,从而实现动态路由和数据传递。
使用场景:

  • RESTful API:在设计RESTful API时,通常会使用@PathVariable来获取资源的唯一标识符(如 ID)。
  • 动态内容:根据 URL 中的变量来动态生成页面或响应内容。

二、拦截器

(一)HandlerInterceptor

        HandlerInterceptor是SpringMVC内置拦截器机制,用于在请求处理的不同阶段插入自定义逻辑。它允许在请求到达控制器之前、控制器处理请求之后以及请求完成之后执行特定的操作。比如:权限验证、日志记录、数据共享等......

使用步骤:

  • 实现HandlerInterceptor接口的组件即可成为拦截器
  • 创建WebMvcConfigurer组件,并配置拦截器的拦截路径
  • 执行顺序:顺序preHandle→目标方法→倒序postHandle→渲染→倒序afterCompletion
  • 只有执行成功的preHandle会倒序执行afterCompletion
  • postHandle、afterCompletion从哪里跑出异常,倒序链路从哪里结束
  • postHandle失败不会影响afterCompletion执行
@Component  // 拦截器还需要配置(告诉SpringMVC,这个拦截器主要拦截什么请求)
public class MyHandlerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("MyHandlerInterceptor...preHandle...");return false; // true表示放行,false表示拦截}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("MyHandlerInterceptor...postHandle...");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("MyHandlerInterceptor...afterCompletion...");}
}
@Configuration  // 专门对SpringMVC底层进行配置
public class MySpringMVCConfig implements WebMvcConfigurer {@AutowiredMyHandlerInterceptor myHandlerInterceptor;/*** 添加拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myHandlerInterceptor).addPathPatterns("/**"); // 拦截所有请求}
}
@CrossOrigin  // 允许跨域请求
@RestController
@RequestMapping("/emp")
public class EmpController {@Autowiredprivate EmpService empService;/*** 查询员工*/@GetMapping("/{empno}")public R queryEmpById(@PathVariable("empno") Integer empno) {System.out.println("查询用户。目标方法执行......");Emp emp = empService.queryById(empno);return R.ok(emp);}
}

(二)拦截器与过滤器的区别(面试题) 

三、异常处理

1.编程式异常处理

编程式异常处理:如果大量业务都需要加异常处理代码会很麻烦

@GetMapping("/hello")
public R hello(@RequestParam(value = "i", defaultValue = "0") Integer i) {try {int j = 10 / i;return R.ok(j);} catch (Exception e) {return R.error(100, "除数不能为0", e.getMessage());}
}

2.声明式异常处理

  1. 如果Controller本类出现异常,会自动在本类中找到有没有@ExceptionHandler标注的方法,如果有,执行这个方法,它的返回值,就是客户端收到的结果;如果发生异常,多个都能处理,就精确的优先。
  2. 异常处理的优先级:本类 > 全局;精确 > 模糊
  3. 如果出现了异常:本类和全局都不能处理,SpringBoot底层对SpringMVC有兜底处理机制:自适应处理(浏览器响应页面、移动端响应JSON)
  4. 最佳实践:编写全局异常处理器,处理所有异常
@RestController
public class HelloController {@GetMapping("/hello")public R hello(@RequestParam(value = "i", defaultValue = "0") Integer i) throws FileNotFoundException {int j = 10 / i;
//        FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lxm\\Desktop\\test.txt");String str = null;str.length();return R.ok(j);}@ExceptionHandler(ArithmeticException.class)public R handlerArithmeticException(ArithmeticException e) {System.out.println("ArithmeticException异常处理");return R.error(100, "除数不能为0", e.getMessage());}@ExceptionHandler(FileNotFoundException.class)public R FileNotFoundException(FileNotFoundException e) {System.out.println("FileNotFoundException异常处理");return R.error(300, "文件找不到", e.getMessage());}@ExceptionHandler(Throwable.class)public R handlerException(Throwable e) {System.out.println("Throwable异常处理");return R.error(500, "其他异常", e.getMessage());}
}
// @ResponseBody
// @ControllerAdvice   // 告诉SpringMVC,这个类是处理全局异常的
@RestControllerAdvice  // 全局异常处理器:相当于@ControllerAdvice + @ResponseBody
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public R handleException(Throwable e) {System.out.println("全局异常处理");return R.error(500, e.getMessage());}@ExceptionHandler(ArithmeticException.class)public R handlerArithmeticException(ArithmeticException e) {System.out.println("算数异常处理");return R.error(100, e.getMessage());}
}

3.异常处理的最终方式

  1. 必须有业务异常类:BusinessException
  2. 必须有异常枚举类:BusinessExceptionEnum列举项目中每个模块将会出现的所有异常情况
  3. 编写业务代码的时候,只需编写正确逻辑,如果出现预期的问题,需要以抛异常的方式中断逻辑并通知上层
  4. 全局异常处理器:GlobalExceptionHandler 处理所有异常,返回给前端约定的JSON数据与错误码

异常枚举类: 

public enum BusinessExceptionEnum {ORDER_NOT_EXIST(10001, "订单不存在"),ORDER_STATUS_ERROR(10002, "订单状态错误"),ORDER_UPDATE_ERROR(10003, "订单更新失败"),ORDER_DELETE_ERROR(10004, "订单删除失败"),ORDER_CREATE_ERROR(10005, "订单创建失败"),ORDER_QUERY_ERROR(10006, "订单查询失败"),ORDER_PAY_ERROR(10007, "订单支付失败"),ORDER_CANCEL_ERROR(10008, "订单取消失败");@Getterprivate Integer code;@Getterprivate String msg;BusinessExceptionEnum(Integer code, String msg) {this.code = code;this.msg = msg;}
}

全局业务异常类: 

@Data
public class BusinessException extends RuntimeException {private Integer code;private String msg;public BusinessException(Integer code, String msg) {super(msg);this.code = code;this.msg = msg;}public BusinessException(BusinessExceptionEnum businessExceptionEnum) {super(businessExceptionEnum.getMsg());this.code = businessExceptionEnum.getCode();this.msg = businessExceptionEnum.getMsg();}
}

业务代码 

@Override
public void update(Emp emp) {// 去数据库查询原来的值Integer empno = emp.getEmpno();if (empno == null) {throw new BusinessException(BusinessExceptionEnum.ORDER_NOT_EXIST);}Emp empById = empDao.getEmpById(empno);if (StringUtils.hasText(empById.getEname())) {empById.setEname(emp.getEname());}empDao.updateEmp(emp);
}

四、SpringMVC原理

五、数据类型转换

1.String转Date类型

@Controller
@RequestMapping("/book")
public class BookController {@GetMapping("/jump")public String jump(){// int i = 1/0;return "book/add";}@PostMapping("/doAdd")@ResponseBodypublic BookModel doAdd(BookModel book){return book;/*** {*   "id": null,*   "name": "红楼梦",*   "ctime": 1735228800000* }*/}@PostMapping("/doAdd2")@ResponseBodypublic Date doAdd2(String name,@DateTimeFormat(pattern = "yyyy-MM-dd") Date ctime){return ctime; // 1735142400000}
}
@Data
public class BookModel {private Integer id;private String name;@DateTimeFormat(pattern = "yyyy-MM-dd")private Date ctime;
}

2.String转LocalDateTime类型

自定义参数类型转换器:

public class StringToDate implements Converter<String, Date> {@Overridepublic Date convert(String s) {//传入的参数,等待被转换的2024-12-27 字符串System.out.println(s);SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");Date date = null;try {date = simpleDateFormat.parse(s);} catch (ParseException e) {throw new RuntimeException(e);}return date;}
}public class StringToDateTime implements Converter<String, LocalDateTime> {@Overridepublic LocalDateTime convert(String s) {return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME);}
}
@Data
public class BookModel {private Integer id;private String name;
//    @DateTimeFormat(pattern = "yyyy-MM-dd")private Date ctime;
//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime utime;
}

3.接收JSON字符串

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.11.2</version>
</dependency>
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.11.2</version>
</dependency>
/*** 接收JSON转换日期时间*/
@PostMapping("/doAdd3")
@ResponseBody
public BookModel doAdd3(@RequestBody BookModel book){return book;
}
@Data
public class BookModel {private Integer id;private String name;// @DateTimeFormat(pattern = "yyyy-MM-dd")private Date ctime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime utime;
}

注意:

JSON时间,可以写2024-01-27 09:09:09
不能写 2024-1-27 9:9:9


http://www.mrgr.cn/news/81686.html

相关文章:

  • llamafactory报错:双卡4090GPU,训练qwen2.5:7B、14B时报错GPU显存不足(out of memory),轻松搞定~~~
  • 虚幻引擎结构之ULevel
  • 【华为OD-E卷 - 货币单位换算 100分(python、java、c++、js、c)】
  • 熊军出席ACDU·中国行南京站,详解SQL管理之道
  • EasyExcel 模板+公式填充
  • SQL—leetcode—175. 组合两个表
  • esp32学习:用虫洞ESP32S3-EYE开发板快速实现USB摄像头(UVC免驱)
  • 【HENU】河南大学计院2024 计算机网络 期末复习知识点
  • 中关村科金智能客服机器人如何解决客户个性化需求与标准化服务之间的矛盾?
  • Windows 11 中利用 WSL - Linux 虚拟环境部署 ChatTTS-Enhanced 项目教程
  • 动态规划<四> 回文串问题(含对应LeetcodeOJ题)
  • 动态规划<五> 子数组问题(含对应LeetcodeOJ题)
  • PaddleOCR训练自己的私有数据集(包括标注、制作数据集、训练及应用)
  • 学习笔记(C#基础书籍)-- C#高级应用
  • WEB UI 创建视图
  • 存储过程实现多个分类不同计算规则得到对应的分类、月份和款号
  • 任务8 数据库服务配置与管理
  • C#学习1:初接触,C#的一些基础,和相关报错
  • TrackZone:使用Ultralytics YOLO11在区域内进行对象跟踪
  • 华为麦芒5(安卓6)termux记录 使用ddns-go,alist
  • SpringCloudAlibaba实战入门之路由网关Gateway初体验(十一)
  • C++内存空间_内存管理_
  • mysql 数据库迁移到达梦数据库
  • mybatis-plus自动填充时间的配置类实现
  • 【Compose multiplatform教程07】多平台常用组件和重要组件目录
  • 【软件工程】十万字知识点梳理 | 期末复习专用