<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-11 15:46

          無侵入式 統(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)有前輩想到過了. 你準備解決的問題, 已經(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 可以標記在類和方法上這樣我們就可以跟自由的進行使用了

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


          ????/**
          ?????*?提供對標準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);
          ????}
          }


          源:blog.csdn.net/qq_34347620/article/details/102239179

          版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有。除非無法確認,我們都會標明作者及出處,如有侵權(quán)煩請告知,我們會立即刪除并表示歉意。謝謝!





          感謝閱讀



          瀏覽 34
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                    超碰自拍 | 韩国三级在线视频网址 | 日韩黄色视频播放 | 澳门精品一区二区三区 | 影音先锋亚洲无码AV |