<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 無(wú)侵入式 實(shí)現(xiàn)API接口統(tǒng)一JSON格式返回

          共 5912字,需瀏覽 12分鐘

           ·

          2020-10-12 13:47

          注意文末有最新Java實(shí)戰(zhàn)項(xiàng)目面試題

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

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

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

          因?yàn)轫?xiàng)目已經(jīng)開(kāi)發(fā)了半年多了, 要是全部接口都做修改工作量還是挺大的, 只能用這種無(wú)侵入式的方案來(lái)解決.

          項(xiàng)目源代碼: 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)碼枚舉類(lè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請(qǐng)求?*/
          ????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)碼就能一一對(duì)應(yīng)了便于維護(hù), 有同學(xué)有疑問(wèn)了為什么要用到http狀態(tài)碼呀,因?yàn)槲乙嫒蓓?xiàng)目以前的代碼, 沒(méi)有其他原因, 當(dāng)然其他同學(xué)不喜歡http狀態(tài)碼的可以吧源碼中HttpStatus給刪除了

          定義返回體類(lèi)

          @Getter
          @ToString
          public?class?Result<T>?{
          ????/**?業(yè)務(wù)錯(cuò)誤碼?*/
          ????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);
          ????}
          }

          因?yàn)槭褂脴?gòu)造方法進(jìn)行創(chuàng)建對(duì)象太麻煩了, 我們使用靜態(tài)方法來(lái)創(chuàng)建對(duì)象這樣簡(jiǎn)單明了

          Result實(shí)體返回測(cè)試

          @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)簡(jiǎn)單的實(shí)現(xiàn)了統(tǒng)一JSON格式了, 但是我們也發(fā)現(xiàn)了一個(gè)問(wèn)題了,想要返回統(tǒng)一的JSON格式需要返回Result才可以, 我明明返回Object可以了, 為什么要重復(fù)勞動(dòng), 有沒(méi)有解決方法, 當(dāng)然是有的啦, 下面我們開(kāi)始優(yōu)化我們的代碼吧

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

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

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

          @ResponseBody繼承類(lèi)

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

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

          }

          ResponseBodyAdvice繼承類(lèi)

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

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

          ????/**
          ?????*?判斷類(lèi)或者方法是否使用了?@ResponseResultBody
          ?????*/

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

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

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

          RestControllerAdvice返回測(cè)試

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

          ????/**?測(cè)試重復(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格式了, 就不用每個(gè)返回都返回Result對(duì)象了,直接讓SpringMVC幫助我們進(jìn)行統(tǒng)一的管理, 簡(jiǎn)直完美

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

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

          臥槽, 異常處理, 差點(diǎn)把這茬給忘了, 這個(gè)異常處理就有很多方法了,先看看我?guī)煾档奶幚矸绞? 我剛拿到這個(gè)代碼的時(shí)候很想吐槽, 對(duì)異常類(lèi)的處理這么殘暴的嗎, 直接用PrintWriter直接輸出結(jié)果, 果然是老師傅, 我要是有100個(gè)異常類(lèi), 不得要寫(xiě)100個(gè) 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;
          ????}
          }

          上面的代碼看看還是沒(méi)有問(wèn)題的, 別學(xué)過(guò)去哦,

          異常處理@ResponseStatus(不推薦)

          @ResponseStatus用法如下,可用在Controller類(lèi)和Controller方法上以及Exception類(lèi)上但是這樣的工作量還是挺大的

          @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類(lèi)進(jìn)行改造一下,代碼有點(diǎn)多了

          主要參考了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;

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

          ????/**?當(dāng)類(lèi)或者方法使用了?@ResponseResultBody?就會(huì)調(diào)用這個(gè)方法?*/
          ????@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);
          ????}


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

          ????/**?對(duì)ResultException類(lèi)返回返回結(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);
          ????}

          ????/**?異常類(lèi)的統(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);
          ????}
          }

          參考博客列表:

          https://www.toutiao.com/i6694404645827117572/https://blog.csdn.net/qq_36722039/article/details/80825117http://www.imooc.com/article/260354https://my.oschina.net/wangkang80/blog/1519189

          作者:小魏小魏我們?nèi)ツ抢镅?/em>

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


          ---END---
          文末福利




          瀏覽 113
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                    日韩黄色免费视频 | 亚洲天堂在线视频观看 | 少妇人妻一级A毛片 | 中文字幕日本无码一区 | 91人妻综合 |