<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 無侵入式 實現(xiàn) API 接口統(tǒng)一 JSON 格式返回

          共 5891字,需瀏覽 12分鐘

           ·

          2020-11-24 03:30

          無侵入式 統(tǒng)一返回JSON格式

          其實本沒有沒打算寫這篇博客的,但還是要寫一下寫這篇博客的起因是因為,現(xiàn)在呆著的這家公司居然沒有統(tǒng)一的API返回格式?,詢問主管他居然告訴我用HTTP狀態(tài)碼就夠用了(fxxk),天哪HTTP狀態(tài)碼真的夠用嗎?

          在仔細的閱讀了項目源碼后發(fā)現(xiàn),在API請求的是居然沒有業(yè)務(wù)異常(黑人問好)。好吧 居然入坑了只能遵照項目風(fēng)格了,懶得吐槽了。

          因為項目已經(jīng)開發(fā)了半年多了, 要是全部接口都做修改工作量還是挺大的, 只能用這種無侵入式的方案來解決.

          項目源代碼: https://github.com/469753862/galaxy-blogs/tree/master/code/responseResult

          定義JSON格

          定義返回JSON格式

          后端返回給前端一般情況下使用JSON格式, 定義如下

          {
          ????"code":?200,
          ????"message":?"OK",
          ????"data":?{

          ????}
          }
          • code: 返回狀態(tài)碼
          • message: 返回信息的描述
          • data: 返回值

          定義JavaBean字段

          定義狀態(tài)碼枚舉類

          @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è)務(wù)異常碼?*/
          ????private?Integer?code;
          ????/**?業(yè)務(wù)異常信息描述?*/
          ????private?String?message;

          ????ResultStatus(HttpStatus?httpStatus,?Integer?code,?String?message)?{
          ????????this.httpStatus?=?httpStatus;
          ????????this.code?=?code;
          ????????this.message?=?message;
          ????}
          }

          狀態(tài)碼和信息以及http狀態(tài)碼就能一一對應(yīng)了便于維護, 有同學(xué)有疑問了為什么要用到http狀態(tài)碼呀,因為我要兼容項目以前的代碼, 沒有其他原因, 當(dāng)然其他同學(xué)不喜歡http狀態(tài)碼的可以吧源碼中HttpStatus給刪除了

          定義返回體類

          @Getter
          @ToString
          public?class?Result<T>?{
          ????/**?業(yè)務(wù)錯誤碼?*/
          ????private?Integer?code;
          ????/**?信息描述?*/
          ????private?String?message;
          ????/**?返回參數(shù)?*/
          ????private?T?data;

          ????private?Result(ResultStatus?resultStatus,?T?data)?{
          ????????this.code?=?resultStatus.getCode();
          ????????this.message?=?resultStatus.getMessage();
          ????????this.data?=?data;
          ????}

          ????/**?業(yè)務(wù)成功返回業(yè)務(wù)代碼和描述信息?*/
          ????public?static?Result?success()?{
          ????????return?new?Result(ResultStatus.SUCCESS,?null);
          ????}

          ????/**?業(yè)務(wù)成功返回業(yè)務(wù)代碼,描述和返回的參數(shù)?*/
          ????public?static??Result?success(T?data)?{
          ????????return?new?Result(ResultStatus.SUCCESS,?data);
          ????}

          ????/**?業(yè)務(wù)成功返回業(yè)務(wù)代碼,描述和返回的參數(shù)?*/
          ????public?static??Result?success(ResultStatus?resultStatus,?T?data)?{
          ????????if?(resultStatus?==?null)?{
          ????????????return?success(data);
          ????????}
          ????????return?new?Result(resultStatus,?data);
          ????}

          ????/**?業(yè)務(wù)異常返回業(yè)務(wù)代碼和描述信息?*/
          ????public?static??Result?failure()?{
          ????????return?new?Result(ResultStatus.INTERNAL_SERVER_ERROR,?null);
          ????}

          ????/**?業(yè)務(wù)異常返回業(yè)務(wù)代碼,描述和返回的參數(shù)?*/
          ????public?static??Result?failure(ResultStatus?resultStatus)?{
          ????????return?failure(resultStatus,?null);
          ????}

          ????/**?業(yè)務(wù)異常返回業(yè)務(wù)代碼,描述和返回的參數(shù)?*/
          ????public?static??Result?failure(ResultStatus?resultStatus,?T?data)?{
          ????????if?(resultStatus?==?null)?{
          ????????????return?new?Result(ResultStatus.INTERNAL_SERVER_ERROR,?null);
          ????????}
          ????????return?new?Result(resultStatus,?data);
          ????}
          }

          因為使用構(gòu)造方法進行創(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>?helloResult()?{
          ????????return?Result.success(INFO);
          ????}
          }

          到這里我們已經(jīng)簡單的實現(xiàn)了統(tǒng)一JSON格式了, 但是我們也發(fā)現(xiàn)了一個問題了,想要返回統(tǒng)一的JSON格式需要返回Result才可以, 我明明返回Object可以了, 為什么要重復(fù)勞動, 有沒有解決方法, 當(dāng)然是有的啦, 下面我們開始優(yōu)化我們的代碼吧

          統(tǒng)一返回JSON格式進階-全局處理(@RestControllerAdvice)

          我?guī)煾到?jīng)常告訴我的一句話: “你就是一個小屁孩, 你遇到的問題都已經(jīng)不知道有多少人遇到過了, 你會想到的問題, 已經(jīng)有前輩想到過了. 你準(zhǔn)備解決的問題, 已經(jīng)有人把坑填了”。是不是很雞湯, 是不是很勵志, 讓我對前輩們充滿著崇拜, 事實上他對我說的是: “自己去百度”, 這五個大字, 其實這五個大字已經(jīng)說明上明的B話了, 通過不斷的百度和Google發(fā)現(xiàn)了很多的解決方案.

          我們都知道使用@ResponseBody注解會把返回Object序列化成JSON字符串,就先從這個入手吧, 大致就是在序列化前把Object賦值給Result就可以了, 大家可以觀摩org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice和org.springframework.web.bind.annotation.ResponseBody

          @ResponseBody繼承類

          我們已經(jīng)決定從@ResponseBody注解入手了就創(chuàng)建一個注解類繼承@ResponseBody, 很干凈什么都沒有哈哈,@ResponseResultBody 可以標(biāo)記在類和方法上這樣我們就可以跟自由的進行使用了

          @Retention(RetentionPolicy.RUNTIME)
          @Target({ElementType.TYPE,?ElementType.METHOD})
          @Documented
          @ResponseBody
          public?@interface?ResponseResultBody?{

          }

          ResponseBodyAdvice繼承類

          @RestControllerAdvice
          public?class?ResponseResultBodyAdvice?implements?ResponseBodyAdvice<Object>?{

          ????private?static?final?Class?ANNOTATION_TYPE?=?ResponseResultBody.class;

          ????/**
          ?????*?判斷類或者方法是否使用了?@ResponseResultBody
          ?????*/

          ????@Override
          ????public?boolean?supports(MethodParameter?returnType,?Class>?converterType)?{
          ????????return?AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),?ANNOTATION_TYPE)?||?returnType.hasMethodAnnotation(ANNOTATION_TYPE);
          ????}

          ????/**
          ?????*?當(dāng)類或者方法使用了?@ResponseResultBody?就會調(diào)用這個方法
          ?????*/

          ????@Override
          ????public?Object?beforeBodyWrite(Object?body,?MethodParameter?returnType,?MediaType?selectedContentType,?Class>?selectedConverterType,?ServerHttpRequest?request,?ServerHttpResponse?response)?{
          ????????//?防止重復(fù)包裹的問題出現(xiàn)
          ????????if?(body?instanceof?Result)?{
          ????????????return?body;
          ????????}
          ????????return?Result.success(body);
          ????}
          }

          RestControllerAdvice返回測試

          @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;
          ????}

          ????/**?測試重復(fù)包裹?*/
          ????@GetMapping("result")
          ????public?Result>?helloResult()?{
          ????????return?Result.success(INFO);
          ????}

          ????@GetMapping("helloError")
          ????public?HashMap?helloError()?throws?Exception?{
          ????????throw?new?Exception("helloError");
          ????}

          ????@GetMapping("helloMyError")
          ????public?HashMap?helloMyError()?throws?Exception?{
          ????????throw?new?ResultException();
          ????}
          }

          是不是很神奇, 直接返回Object就可以統(tǒng)一JSON格式了, 就不用每個返回都返回Result對象了,直接讓SpringMVC幫助我們進行統(tǒng)一的管理, 簡直完美

          只想看接口哦, helloError和helloMyError是會直接拋出異常的接口,我好像沒有對異常返回進行統(tǒng)一的處理哦

          統(tǒng)一返回JSON格式進階-異常處理(@ExceptionHandler))

          臥槽, 異常處理, 差點把這茬給忘了, 這個異常處理就有很多方法了,先看看我?guī)煾档奶幚矸绞? 我剛拿到這個代碼的時候很想吐槽, 對異常類的處理這么殘暴的嗎, 直接用PrintWriter直接輸出結(jié)果, 果然是老師傅, 我要是有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(),?"服務(wù)器異常"));
          ????????}
          ????????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;
          ????}
          }

          上面的代碼看看還是沒有問題的, 別學(xué)過去哦,

          異常處理@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<Object>?{

          ????private?static?final?Class?ANNOTATION_TYPE?=?ResponseResultBody.class;

          ????/**?判斷類或者方法是否使用了?@ResponseResultBody?*/
          ????@Override
          ????public?boolean?supports(MethodParameter?returnType,?Class>?converterType)?{
          ????????return?AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),?ANNOTATION_TYPE)?||?returnType.hasMethodAnnotation(ANNOTATION_TYPE);
          ????}

          ????/**?當(dāng)類或者方法使用了?@ResponseResultBody?就會調(diào)用這個方法?*/
          ????@Override
          ????public?Object?beforeBodyWrite(Object?body,?MethodParameter?returnType,?MediaType?selectedContentType,?Class>?selectedConverterType,?ServerHttpRequest?request,?ServerHttpResponse?response)?{
          ????????if?(body?instanceof?Result)?{
          ????????????return?body;
          ????????}
          ????????return?Result.success(body);
          ????}


          ????/**
          ?????*?提供對標(biāo)準(zhǔn)Spring?MVC異常的處理
          ?????*
          ?????*?@param?ex??????the?target?exception
          ?????*?@param?request?the?current?request
          ?????*/

          ????@ExceptionHandler(Exception.class)
          ????public?final?ResponseEntity>?exceptionHandler(Exception?ex,?WebRequest?request)?{
          ????????log.error("ExceptionHandler:?{}",?ex.getMessage());
          ????????HttpHeaders?headers?=?new?HttpHeaders();
          ????????if?(ex?instanceof?ResultException)?{
          ????????????return?this.handleResultException((ResultException)?ex,?headers,?request);
          ????????}
          ????????//?TODO:?2019/10/05?galaxy?這里可以自定義其他的異常攔截
          ????????return?this.handleException(ex,?headers,?request);
          ????}

          ????/**?對ResultException類返回返回結(jié)果的處理?*/
          ????protected?ResponseEntity>?handleResultException(ResultException?ex,?HttpHeaders?headers,?WebRequest?request)?{
          ????????Result?body?=?Result.failure(ex.getResultStatus());
          ????????HttpStatus?status?=?ex.getResultStatus().getHttpStatus();
          ????????return?this.handleExceptionInternal(ex,?body,?headers,?status,?request);
          ????}

          ????/**?異常類的統(tǒng)一處理?*/
          ????protected?ResponseEntity>?handleException(Exception?ex,?HttpHeaders?headers,?WebRequest?request)?{
          ????????Result?body?=?Result.failure();
          ????????HttpStatus?status?=?HttpStatus.INTERNAL_SERVER_ERROR;
          ????????return?this.handleExceptionInternal(ex,?body,?headers,?status,?request);
          ????}

          ????/**
          ?????*?org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler#handleExceptionInternal(java.lang.Exception,?java.lang.Object,?org.springframework.http.HttpHeaders,?org.springframework.http.HttpStatus,?org.springframework.web.context.request.WebRequest)
          ?????*?


          ?????*?A?single?place?to?customize?the?response?body?of?all?exception?types.
          ?????*?

          The?default?implementation?sets?the?{@link?WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
          ?????*?request?attribute?and?creates?a?{@link?ResponseEntity}?from?the?given
          ?????*?body,?headers,?and?status.
          ?????*/
          ????protected?ResponseEntity>?handleExceptionInternal(
          ????????????Exception?ex,?Result?body,?HttpHeaders?headers,?HttpStatus?status,?WebRequest?request)?{

          ????????if?(HttpStatus.INTERNAL_SERVER_ERROR.equals(status))?{
          ????????????request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE,?ex,?WebRequest.SCOPE_REQUEST);
          ????????}
          ????????return?new?ResponseEntity<>(body,?headers,?status);
          ????}
          }


          題外話: 目前小哈正在個人博客(新搭建的網(wǎng)站,域名就是犬小哈的拼音)?www.quanxiaoha.com?上更新《Go語言教程》,畢竟Go自帶天然的并發(fā)優(yōu)勢,后端的同學(xué)還是要學(xué)一下的,這個教程系列小哈會一直更新下去,目前已經(jīng)更新到 Go語言的基礎(chǔ)語法了,歡迎小伙伴們訪問哦~

          END


          有熱門推薦?

          1.?看完后,我才明白 Redis 為什么默認 16 個數(shù)據(jù)庫?

          2.?「干貨總結(jié)」程序員必知必會的十大排序算法

          3.?淦!女票問我能不能開發(fā)一個微信朋友圈、微博?

          4.?小團隊如何落地敏捷開發(fā)

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點“在看”,關(guān)注公眾號并回復(fù)?Java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    精品久久久久久久久久久久 | 女人精品视频 | 俺也去色官网在线播放 | 国产精品高清无码 | 翔田千里无码破解在线 |