Java?Controller實(shí)現(xiàn)參數(shù)驗(yàn)證與統(tǒng)一異常處理流程詳細(xì)講解
最近開(kāi)發(fā)了比較多的接口,因?yàn)闆](méi)有可參考的案例,所以一開(kāi)始一直按照我的理解進(jìn)行開(kāi)發(fā)。開(kāi)發(fā)多了發(fā)現(xiàn)自己每個(gè)結(jié)果都寫了相同的代碼:try() {} catch() {}, 和關(guān)于參數(shù)判空的:StringUtils.empty(xxx)。開(kāi)發(fā)結(jié)束后自然想下次更加優(yōu)雅的開(kāi)發(fā)。因此,使用了springboot的參數(shù)驗(yàn)證和統(tǒng)一異常處理。
一,前期數(shù)據(jù)及類準(zhǔn)備
1.1 統(tǒng)一狀態(tài)碼
對(duì)于不同的返回類型,我們應(yīng)該要有不同對(duì)應(yīng)的狀態(tài)碼。接口的返回類型在統(tǒng)一狀態(tài)碼中必須存在。
package com.lmc.common.enums;
/**
* @Description: TODO 接口API返回狀態(tài)碼枚舉
* @version: 1.0
*/
public enum ResultCodeEnum {
SUCCESS(1000, "請(qǐng)求成功"),
FAILURE(1001, "請(qǐng)求失敗"),
VALIDATE_PARAMS_ERROR(1002, "參數(shù)校驗(yàn)失敗");
private int code;
private String msg;
ResultCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 獲取code
* @return
*/
public int getCode() {
return code;
}
/**
* 獲取信息
* @return
*/
public String getMsg() {
return msg;
}
}
1.2 統(tǒng)一返回格式
統(tǒng)一狀態(tài)碼完成后,還需要定義統(tǒng)一返回格式,為了前端的方便調(diào)用
package com.lmc.common.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.lmc.common.enums.ResultCodeEnum;
import lombok.Data;
import java.util.Date;
/**
* @Description: TODO 接口返回結(jié)果類型
* @version: 1.0
*/
@Data
public class ResultVo {
/**
* 狀態(tài)碼
*/
private int code;
/**
* 狀態(tài)碼信息
*/
private String msg;
/**
* 返回描述信息(預(yù)備為調(diào)用失敗的情況下提供詳細(xì)的失敗原因)
*/
private String desc;
/**
* 返回?cái)?shù)據(jù)
*/
private Object data;
/**
* 接口調(diào)用結(jié)束時(shí)間
*/
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
private Date searchTime;
public ResultVo(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
this.searchTime = new Date();
}
/**
* 調(diào)用成功時(shí)返回
* @param data
* @return
*/
public static ResultVo success(Object data) {
return new ResultVo(ResultCodeEnum.SUCCESS.getCode(), ResultCodeEnum.SUCCESS.getMsg(), data);
}
/**
* 調(diào)用失敗時(shí)返回
* @param data
* @return
*/
public static ResultVo fail(Object data) {
return new ResultVo(ResultCodeEnum.FAILURE.getCode(), ResultCodeEnum.FAILURE.getMsg(), data);
}
/**
* 調(diào)用時(shí)指定狀態(tài)碼
* @param enums
* @param data
* @return
*/
public static ResultVo result(ResultCodeEnum enums, Object data) {
return new ResultVo(enums.getCode(), enums.getMsg(), data);
}
public ResultVo withDesc(String desc) {
this.desc = desc;
return this;
}
}
1.3 自定義接口API異常類
然后再自定義接口的異常類,當(dāng)然也可以不用,看個(gè)人喜好
package pers.lmc.tools2.provider.exception;
import com.lmc.common.enums.ResultCodeEnum;
/**
* @Description: TODO API異常類
* @version: 1.0
*/
public class ApiException extends RuntimeException{
private int code;
private String msg;
public ApiException(String msg) {
super(msg);
this.code = ResultCodeEnum.FAILURE.getCode();
this.msg = ResultCodeEnum.FAILURE.getMsg();
}
public ApiException(ResultCodeEnum enums, String msg) {
super(msg);
this.code = enums.getCode();
this.msg = enums.getMsg();
}
}
1.4 參數(shù)封裝類
為了調(diào)試參數(shù)驗(yàn)證,還需要自定義一個(gè)參數(shù)的封裝類
package pers.lmc.tools2.provider.vo;
import lombok.Data;
/**
* @Description: TODO
* @version: 1.0
*/
@Data
public class Param01Vo {
private String name;
private Integer age;
private Short sex;
}
二,參數(shù)驗(yàn)證
參數(shù)驗(yàn)證需要用到springboot的validation依賴
2.1 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 關(guān)于校驗(yàn) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
2.2 修改參數(shù)封裝類
package pers.lmc.tools2.provider.vo;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* @Description: TODO
* @version: 1.0
*/
@Data
public class Param01Vo {
@NotNull(message = "名稱不能為空")
@Size(min = 1, max = 50, message = "名稱name長(zhǎng)度必須是1-50個(gè)字符")
private String name;
@NotNull(message = "年齡age不能為空")
@Min(value = 10, message = "年齡age不能低于10歲")
@Max(value = 25, message = "年齡age不能超過(guò)25歲")
private Integer age;
@Min(value = 0, message = "性別sex只能是0和1,0=女1=男")
@Max(value = 1, message = "性別sex只能是0和1,0=女1=男")
private Short sex;
}
在這里對(duì)該封裝類的三個(gè)參數(shù)都做了限制
2.3 controller
在controller中對(duì)參數(shù)做驗(yàn)證時(shí),需要在類上使用注解@Validated,同時(shí)在接口的該參數(shù)也使用注解@Valid
package pers.lmc.tools2.provider.controller;
import com.lmc.common.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pers.lmc.tools2.provider.vo.Param01Vo;
import javax.validation.Valid;
/*
* @Description: TODO
* @version: 1.0
*/
@RestController
@Validated
@RequestMapping("/valicate")
@Slf4j
public class ValicateController {
@PostMapping("/add")
public ResultVo addParam01(@Valid @RequestBody Param01Vo param01Vo) {
log.info("執(zhí)行add()方法,參數(shù):" + param01Vo.toString());
return ResultVo.success(param01Vo);
}
}
2.4 測(cè)試
開(kāi)發(fā)完成,準(zhǔn)備測(cè)試,到APIPost上 訪問(wèn) http://localhost:9003/provider/valicate/add,帶上參數(shù):
{
"name":"lmc",
"age": 22,
"sex": 1
}
訪問(wèn)成功,返回結(jié)果如下:
{
"code": 1000,
"msg": "請(qǐng)求成功",
"desc": null,
"data": {
"name": "lmc",
"age": 22,
"sex": 1
},
"searchTime": "2022-06-26 19:59:55"
}
如果參數(shù)輸入不正確,例如:
{
"name":"",
"age": 220,
"sex": 2
}
得到結(jié)果如下:
{
"timestamp": "2022-06-26T12:02:21.748+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/provider/valicate/add"
}
日志是這樣的:
2022-06-26 20:02:21 [http-nio-9003-exec-1] WARN o.s.w.s.m.support.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.lmc.common.vo.ResultVo pers.lmc.tools2.provider.controller.ValicateController.addParam01(pers.lmc.tools2.provider.vo.Param01Vo) with 3 errors: [Field error in object 'param01Vo' on field 'sex': rejected value [2]; codes [Max.param01Vo.sex,Max.sex,Max.java.lang.Short,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [param01Vo.sex,sex]; arguments []; default message [sex],1]; default message [性別sex只能是0和1,0=女1=男]] [Field error in object 'param01Vo' on field 'age': rejected value [220]; codes [Max.param01Vo.age,Max.age,Max.java.lang.Integer,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [param01Vo.age,age]; arguments []; default message [age],25]; default message [年齡age不能超過(guò)25歲]] [Field error in object 'param01Vo' on field 'name': rejected value []; codes [Size.param01Vo.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [param01Vo.name,name]; arguments []; default message [name],50,1]; default message [名稱name長(zhǎng)度必須是1-50個(gè)字符]] ]
拋出了MethodArgumentNotValidException異常。
雖然參數(shù)錯(cuò)誤時(shí)確實(shí)被攔截了,但格式已經(jīng)和我們想要返回的不一致了。這個(gè)時(shí)候,就需要用到統(tǒng)一異常處理了。
三,統(tǒng)一異常處理
3.1 方法參數(shù)驗(yàn)證異常處理
通過(guò)以上的問(wèn)題,我們可以設(shè)置controller的統(tǒng)一異常處理,當(dāng)出現(xiàn)參數(shù)驗(yàn)證錯(cuò)誤時(shí),就捕獲MethodArgumentNotValidException異常,然后我們自己做處理。
package pers.lmc.tools2.provider.aop;
import com.lmc.common.enums.ResultCodeEnum;
import com.lmc.common.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import pers.lmc.tools2.provider.exception.ApiException;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Description: TODO
* @version: 1.0
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 處理所有校驗(yàn)失敗的異常(MethodArgumentNotValidException異常)
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResultVo handleBindGetException(MethodArgumentNotValidException e) {
// 獲取所有異常參數(shù)
List<String> errors = e.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());
return ResultVo.result(ResultCodeEnum.VALIDATE_PARAMS_ERROR, null).withDesc("參數(shù)校驗(yàn)失敗:" + errors);
}
/**
* 處理自定義APIException異常
* @param e
* @return
*/
@ExceptionHandler(value = ApiException.class)
public ResultVo handleApiException(ApiException e) {
return ResultVo.fail(null).withDesc(e.getMessage());
}
/**
* 處理其他異常
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
public ResultVo handleException(Exception e) {
log.info("執(zhí)行到統(tǒng)一處理方法...");
return ResultVo.fail(null).withDesc(e.getMessage());
}
}通過(guò)以上配置,再次以非法參數(shù)傳輸時(shí),會(huì)報(bào)出以下錯(cuò)誤:
{
"code": 1002,
"msg": "參數(shù)校驗(yàn)失敗",
"desc": "參數(shù)校驗(yàn)失敗:[性別sex只能是0和1,0=女1=男, 名稱name長(zhǎng)度必須是1-50個(gè)字符, 年齡age不能超過(guò)25歲]",
"data": null,
"searchTime": "2022-06-26 20:08:22"
}
這個(gè)時(shí)候格式已經(jīng)我們想要的返回格式了。
3.2 其他異常處理
剛剛我們嘗試的是方法的參數(shù)驗(yàn)證異常的處理,對(duì)于程序還可能出現(xiàn)的錯(cuò)誤,配置統(tǒng)一異常處理后也不需要使用try{} catch() {},因?yàn)槲覀円呀?jīng)在全局異常處理類中配置了:
/**
* 處理其他異常
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
public ResultVo handleException(Exception e) {
log.info("執(zhí)行到統(tǒng)一處理方法...");
return ResultVo.fail(null).withDesc(e.getMessage());
}
這個(gè)時(shí)候在程序中拋出其他異常,就會(huì)執(zhí)行到這里的代碼,同樣返回我們想要的格式。舉例如下
修改controller接口:
@PostMapping("/add")
public ResultVo addParam01(@Valid @RequestBody Param01Vo param01Vo) {
log.info("執(zhí)行add()方法,參數(shù):" + param01Vo.toString());
int k = 1/0; // 調(diào)用該接口時(shí)執(zhí)行到這里會(huì)拋出異常
return ResultVo.success(param01Vo);
}
調(diào)用接口返回結(jié)果:
{
"code": 1001,
"msg": "請(qǐng)求失敗",
"desc": "/ by zero",
"data": null,
"searchTime": "2022-06-26 20:13:51"
}
到此這篇關(guān)于Java Controller實(shí)現(xiàn)參數(shù)驗(yàn)證與統(tǒng)一異常處理流程詳細(xì)講解的文章就介紹到這了,更多相關(guān)Java Controller參數(shù)驗(yàn)證與異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java值得使用Lambda的8個(gè)場(chǎng)景合集
可能對(duì)不少人來(lái)說(shuō),Lambda顯得陌生又復(fù)雜,覺(jué)得Lambda會(huì)導(dǎo)致代碼可讀性下降,但畢竟2023年了,JDK都出了那么多新版本,是時(shí)候試試Lambda了2023-08-08
如何開(kāi)發(fā)基于Netty的HTTP/HTTPS應(yīng)用程序
HTTP/HTTPS是最常見(jiàn)的協(xié)議套件之一,并且隨著智能手機(jī)的成功,它的應(yīng)用也日益廣泛,因?yàn)閷?duì)于任何公司來(lái)說(shuō),擁有一個(gè)可以被移動(dòng)設(shè)備訪問(wèn)的網(wǎng)站幾乎是必須的。下面就來(lái)看看如何開(kāi)發(fā)基于Netty的HTTP/HTTPS應(yīng)用程序2021-06-06
java讀取Excel導(dǎo)入去除空行簡(jiǎn)單方法
這篇文章主要給大家介紹了關(guān)于java讀取Excel導(dǎo)入去除空行的簡(jiǎn)單方法,在日常開(kāi)發(fā)中,想必都遇到過(guò)批處理的需求,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考下2023-07-07

