一、RESTful?
(一)RESTful概述
????????RESTful是一種軟件架構風格,用于設計網絡應用程序。REST是“Representational State Transfer”的縮寫,中文意思是“表現層狀態轉移”。它基于客戶端-服務器模型和無狀態操作,以及使用HTTP請求來處理數據。RESTful架構風格強調利用HTTP協議的四個主要方法(GET、POST、PUT、DELETE)來實現資源的訪問和操作。以下是RESTful架構的一些核心原則:
客戶端-服務器分離:客戶端和服務器之間的交互應該是簡單的,服務器端負責存儲數據和業務邏輯,客戶端負責展示。
無狀態:每個請求從客戶端到服務器必須包含所有必要的信息,以便服務器能夠理解請求并獨立地處理它,不依賴于之前的任何請求。
可緩存:數據被標記為可緩存或不可緩存。如果數據被標記為可緩存,那么客戶端可以緩存數據以提高效率。
統一接口:系統組件之間的交互通過統一的接口進行,這簡化了整體系統架構,使得系統更容易理解、開發和使用。
分層系統:客戶端不能直接了解它所消費的服務之外的任何服務器信息,也不應該知道它的數據是來自一個服務器還是多個服務器。
按需代碼(可選):服務器可以按需向客戶端發送代碼,比如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.聲明式異常處理
- 如果Controller本類出現異常,會自動在本類中找到有沒有@ExceptionHandler標注的方法,如果有,執行這個方法,它的返回值,就是客戶端收到的結果;如果發生異常,多個都能處理,就精確的優先。
- 異常處理的優先級:本類 > 全局;精確 > 模糊
- 如果出現了異常:本類和全局都不能處理,SpringBoot底層對SpringMVC有兜底處理機制:自適應處理(瀏覽器響應頁面、移動端響應JSON)
- 最佳實踐:編寫全局異常處理器,處理所有異常
@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.異常處理的最終方式
- 必須有業務異常類:BusinessException
- 必須有異常枚舉類:BusinessExceptionEnum列舉項目中每個模塊將會出現的所有異常情況
- 編寫業務代碼的時候,只需編寫正確邏輯,如果出現預期的問題,需要以拋異常的方式中斷邏輯并通知上層
- 全局異常處理器: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