Spring Boot 無侵入式 實現API接口統一JSON格式返回,倍爽!

作者:小魏小魏我們去那里呀
blog.csdn.net/qq_34347620/article/details/102239179
無侵入式 統一返回JSON格式
其實本沒有沒打算寫這篇博客的,但還是要寫一下寫這篇博客的起因是因為,現在呆著的這家公司居然沒有統一的API返回格式,詢問主管他居然告訴我用HTTP狀態(tài)碼就夠用了(fxxk),天哪HTTP狀態(tài)碼真的夠用嗎?在仔細的閱讀了項目源碼后發(fā)現,在API請求的是居然沒有業(yè)務異常(黑人問好)。好吧 居然入坑了只能遵照項目風格了,懶得吐槽了。
因為項目已經開發(fā)了半年多了, 要是全部接口都做修改工作量還是挺大的, 只能用這種無侵入式的方案來解決.
定義JSON格式
定義返回JSON格式
后端返回給前端一般情況下使用JSON格式, 定義如下
{
?"code":?200,?"message":?"OK",?"data":?{?}
?}
code: 返回狀態(tài)碼
message: 返回信息的描述
data: 返回值
定義JavaBean字段
定義狀態(tài)碼枚舉類
@ToString
@Getter
@ToString
@Getter
public?enum?ResultStatus?{
????SUCCESS(HttpStatus.OK,?200,?"OK"),
????BAD_REQUEST(HttpStatus.BAD_REQUEST,?400,?"Bad?Request"),
????INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,?500,?"Internal?Server?Error"),;
????/**?返回的HTTP狀態(tài)碼,??符合http請求?*/
????private?HttpStatus?httpStatus;
????/**?業(yè)務異常碼?*/
????private?Integer?code;
????/**?業(yè)務異常信息描述?*/
????private?String?message;
????ResultStatus(HttpStatus?httpStatus,?Integer?code,?String?message)?{
????????this.httpStatus?=?httpStatus;
????????this.code?=?code;
????????this.message?=?message;
????}
}
狀態(tài)碼和信息以及http狀態(tài)碼就能一一對應了便于維護, 有同學有疑問了為什么要用到http狀態(tài)碼呀,因為我要兼容項目以前的代碼, 沒有其他原因, 當然其他同學不喜歡http狀態(tài)碼的可以吧源碼中HttpStatus給刪除了定義返回體類
@Getter
@ToString
public?class?Result?{
????/**?業(yè)務錯誤碼?*/
????private?Integer?code;
????/**?信息描述?*/
????private?String?message;
????/**?返回參數?*/
????private?T?data;
????private?Result(ResultStatus?resultStatus,?T?data)?{
????????this.code?=?resultStatus.getCode();
????????this.message?=?resultStatus.getMessage();
????????this.data?=?data;
????}
????/**?業(yè)務成功返回業(yè)務代碼和描述信息?*/
????public?static?Result?success()?{
????????return?new?Result(ResultStatus.SUCCESS,?null);
????}
????/**?業(yè)務成功返回業(yè)務代碼,描述和返回的參數?*/
????public?static??Result?success(T?data)?{
????????return?new?Result(ResultStatus.SUCCESS,?data);
????}
????/**?業(yè)務成功返回業(yè)務代碼,描述和返回的參數?*/
????public?static??Result?success(ResultStatus?resultStatus,?T?data)?{
????????if?(resultStatus?==?null)?{
????????????return?success(data);
????????}
????????return?new?Result(resultStatus,?data);
????}
????/**?業(yè)務異常返回業(yè)務代碼和描述信息?*/
????public?static??Result?failure()?{
????????return?new?Result(ResultStatus.INTERNAL_SERVER_ERROR,?null);
????}
????/**?業(yè)務異常返回業(yè)務代碼,描述和返回的參數?*/
????public?static??Result?failure(ResultStatus?resultStatus)?{
????????return?failure(resultStatus,?null);
????}
????/**?業(yè)務異常返回業(yè)務代碼,描述和返回的參數?*/
????public?static??Result?failure(ResultStatus?resultStatus,?T?data)?{
????????if?(resultStatus?==?null)?{
????????????return?new?Result(ResultStatus.INTERNAL_SERVER_ERROR,?null);
????????}
????????return?new?Result(resultStatus,?data);
????}
}
因為使用構造方法進行創(chuàng)建對象太麻煩了, 我們使用靜態(tài)方法來創(chuàng)建對象這樣簡單明了
Result實體返回測試
@RestController
@RequestMapping("/hello")
public?class?HelloController?{
????private?static?final?HashMap?INFO;
????static?{
????????INFO?=?new?HashMap<>();
????????INFO.put("name",?"galaxy");
????????INFO.put("age",?"70");
????}
????@GetMapping("/hello")
????public?Map?hello()?{
????????return?INFO;
????}
????@GetMapping("/result")
????@ResponseBody
????public?Result 到這里我們已經簡單的實現了統一JSON格式了, 但是我們也發(fā)現了一個問題了,想要返回統一的JSON格式需要返回Result Object才可以, 我明明返回Object可以了, 為什么要重復勞動, 有沒有解決方法, 當然是有的啦, 下面我們開始優(yōu)化我們的代碼吧
統一返回JSON格式進階-全局處理(@RestControllerAdvice)
我?guī)煾到洺8嬖V我的一句話: “你就是一個小屁孩, 你遇到的問題都已經不知道有多少人遇到過了, 你會想到的問題, 已經有前輩想到過了. 你準備解決的問題, 已經有人把坑填了”。是不是很雞湯, 是不是很勵志, 讓我對前輩們充滿著崇拜, 事實上他對我說的是: “自己去百度”, 這五個大字, 其實這五個大字已經說明上明的B話了, 通過不斷的百度和Google發(fā)現了很多的解決方案.
我們都知道使用@ResponseBody注解會把返回Object序列化成JSON字符串,就先從這個入手吧, 大致就是在序列化前把Object賦值給Result Object就可以了, 大家可以觀摩org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice和org.springframework.web.bind.annotation.ResponseBody
@ResponseBody繼承類
我們已經決定從@ResponseBody注解入手了就創(chuàng)建一個注解類繼承@ResponseBody, 很干凈什么都沒有哈哈,@ResponseResultBody 可以標記在類和方法上這樣我們就可以跟自由的進行使用了
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,?ElementType.METHOD})
@Documented
@ResponseBody
public?@interface?ResponseResultBody?{
}
ResponseBodyAdvice繼承類
@RestControllerAdvice
public?class?ResponseResultBodyAdvice?implements?ResponseBodyAdviceRestControllerAdvice返回測試
@RestController
@RequestMapping("/helloResult")
@ResponseResultBody
public?class?HelloResultController?{
????private?static?final?HashMap?INFO;
????static?{
????????INFO?=?new?HashMap();
????????INFO.put("name",?"galaxy");
????????INFO.put("age",?"70");
????}
????@GetMapping("hello")
????public?HashMap?hello()?{
????????return?INFO;
????}
????/**?測試重復包裹?*/
????@GetMapping("result")
????public?Result 是不是很神奇, 直接返回Object就可以統一JSON格式了, 就不用每個返回都返回Result
只想看接口哦, helloError和helloMyError是會直接拋出異常的接口,我好像沒有對異常返回進行統一的處理哦
統一返回JSON格式進階-異常處理(@ExceptionHandler))
臥槽, 異常處理, 差點把這茬給忘了, 這個異常處理就有很多方法了,先看看我?guī)煾档奶幚矸绞? 我剛拿到這個代碼的時候很想吐槽, 對異常類的處理這么殘暴的嗎, 直接用PrintWriter直接輸出結果, 果然是老師傅, 我要是有100個異常類, 不得要寫100個 if else了. 趕緊改改睡吧
@Configuration
public?class?MyExceptionHandler?implements?HandlerExceptionResolver?{
????public?ModelAndView?resolveException(HttpServletRequest?request,?HttpServletResponse?response,
?????????????????????????????????????????Object?handler,?Exception?ex)?{
????????PrintWriter?out?=?getPrintWrite(response);
????????if?(ex?instanceof?XXXException)?{
????????????out.write(JsonUtil.formatJson(ResultEnum.PAY_ERROR.getCode(),?ex.getMessage()));
????????}?else?{
????????????out.write(JsonUtil.formatJson(ResultEnum.FAIL.getCode(),?"服務器異常"));
????????}
????????if?(null?!=?out)?{
????????????out.close();
????????}
????????return?mav;
????}
????private?PrintWriter?getPrintWrite(HttpServletResponse?response)?{
????????PrintWriter?out?=?null;
????????try?{
????????????response.setHeader("Content-type",?"text/html;charset=UTF-8");
????????????response.setCharacterEncoding("UTF-8");
????????????out?=?response.getWriter();
????????}?catch?(IOException?e)?{
????????????log.error("PrintWriter?is?exception",?e);
????????}
????????return?out;
????}
}
上面的代碼看看還是沒有問題的, 別學過去哦,
異常處理@ResponseStatus(不推薦)@ResponseStatus用法如下,可用在Controller類和Controller方法上以及Exception類上但是這樣的工作量還是挺大的
@RestController
@RequestMapping("/error")
@ResponseStatus(value?=?HttpStatus.INTERNAL_SERVER_ERROR,?reason?=?"Java的異常")
public?class?HelloExceptionController?{
????private?static?final?HashMap?INFO;
????static?{
????????INFO?=?new?HashMap();
????????INFO.put("name",?"galaxy");
????????INFO.put("age",?"70");
????}
????@GetMapping()
????public?HashMap?helloError()?throws?Exception?{
????????throw?new?Exception("helloError");
????}
????@GetMapping("helloJavaError")
????@ResponseStatus(value?=?HttpStatus.INTERNAL_SERVER_ERROR,?reason?=?"Java的異常")
????public?HashMap?helloJavaError()?throws?Exception?{
????????throw?new?Exception("helloError");
????}
????@GetMapping("helloMyError")
????public?HashMap?helloMyError()?throws?Exception?{
????????throw?new?MyException();
????}
}
@ResponseStatus(value?=?HttpStatus.INTERNAL_SERVER_ERROR,?reason?=?"自己定義的異常")
class?MyException?extends?Exception?{
}
全局異常處理@ExceptionHandler(推薦)把ResponseResultBodyAdvice類進行改造一下,代碼有點多了 主要參考了org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleException()方法, 有空可以看一下
@Slf4j
@RestControllerAdvice
public?class?ResponseResultBodyAdvice?implements?ResponseBodyAdvice“參考https://www.toutiao.com/i6694404645827117572/ https://blog.csdn.net/qq_36722039/article/details/80825117 http://www.imooc.com/article/260354 https://my.oschina.net/wangkang80/blog/1519189
”

好文章,我在看
點擊閱讀原文,獲免費JVM+MySQL+設計模式+分布式+微服務完整面試資料
好文章,我在看
點擊閱讀原文,獲免費JVM+MySQL+設計模式+分布式+微服務完整面試資料
點擊閱讀原文,獲免費JVM+MySQL+設計模式+分布式+微服務完整面試資料
