當前位置: 妍妍網 > 碼農

如何優雅的寫 Controller 層程式碼?簡單...

2024-05-10碼農

大家好,我是一航!

程式設計師有沒有可能成為藝術家,答案是肯定的!因為編程也是一門藝術,大佬們的程式碼,總是能讓人用起來舒服、讀起來易懂,給人一種賞心悅目的感覺,看完總是忍不住說上一句:妙啊!

其實不管做什麽都會有很多技巧,寫程式碼也不例外!本篇就來介紹一下,如何寫好一個 controller ,讓你的介面變的更加優雅!

一個完整的後端請求由 4 部份組成:

  • 介面地址(也就是 URL 地址)

  • 請求方式(一般就是 get、set,當然還有 put、delete)

  • 請求數據(request,有 head 跟 body)

  • 響應數據(response)

  • 本篇將解決以下 3 個問題:

  • 當接收到請求時,如何優雅的校驗參數

  • 返回響應數據該如何統一的進行處理

  • 接收到請求,處理業務邏輯時丟擲了異常又該如何處理

  • 1 Controller 層參數接收

    太基礎了,可以跳過...

    常見的請求就分為 get 跟 post 兩種:

    @RestController
    @RequestMapping("/product/product-info")
    public classProductInfoController{
    @Autowired
    ProductInfoService productInfoService;
    @GetMapping("/findById")
    public ProductInfoQueryVo findById(Integer id){
    ...
    }
    @PostMapping("/page")
    public IPage findPage(Page page, ProductInfoQueryVo vo){
    ...
    }
    }

  • RestController:

    之前解釋過,@RestController=@Controller+ResponseBody。

    加上這個註解,springboot 就會吧這個類當成 controller 進行處理,然後把所有返回的參數放到 ResponseBody 中。

  • @RequestMapping:

    請求的字首,也就是所有該 Controller 下的請求都需要加上 /product/product-info 的字首。

  • @GetMapping("/findById"):

    標誌這是一個 get 請求,並且需要透過 /findById 地址才可以存取到。

  • @PostMapping("/page"):

    同理,表示是個 post 請求。

    參數:至於參數部份,只需要寫上 ProductInfoQueryVo,前端過來的 json 請求便會透過對映賦值到對應的物件中,例如請求這麽寫,productId 就會自動被對映到 vo 對應的內容當中。

    size : 1
    current : 1
    productId : 1
    productName : 泡腳

  • 2 統一狀態碼

    返回格式

    為了跟前端妹妹打好關系,我們通常需要對後端返回的數據進行包裝一下,增加一下狀態碼,狀態資訊,這樣前端妹妹接收到數據就可以根據不同的狀態碼,判斷響應數據狀態,是否成功是否異常進行不同的顯示。

    當然這讓你擁有了更多跟前端妹妹的交流機會,假設我們約定了 1000 就是成功的意思。

    如果你不封裝,那麽返回的數據是這樣子的:

    {
    "productId"1,
    "productName""泡腳",
    "productPrice"100.00,
    "productDescription""中藥泡腳加按摩",
    "productStatus"0,
    }

    經過封裝以後時這樣子的:

    {
    "code"1000,
    "msg""請求成功",
    "data": {
    "productId"1,
    "productName""泡腳",
    "productPrice"100.00,
    "productDescription""中藥泡腳加按摩",
    "productStatus"0,
    }
    }

    封裝 ResultVo

    這些狀態碼肯定都是要預先編好的,怎麽編呢?寫個常量 1000?還是直接寫死 1000?

    要這麽寫就真的書白讀的了,寫狀態碼當然是用列舉拉:

    1. 首先先定義一個狀態碼的介面,所有狀態碼都需要實作它,有了標準才好做事:

      publicinterfaceStatusCode{
      publicintgetCode();
      public String getMsg();
      }

    2. 然後去找前端妹妹,跟他約定好狀態碼(這可能是你們唯一的約定了)列舉類嘛,當然不能有 setter 方法了,因此我們不能在用 @Data 註解了,我們要用 @Getter。

      @Getter
      publicenum ResultCode implements StatusCode{
      SUCCESS(1000"請求成功"),
      FAILED(1001"請求失敗"),
      VALIDATE_ERROR(1002"參數校驗失敗"),
      RESPONSE_PACK_ERROR(1003"response返回包裝失敗");
      privateint code;
      private String msg;
      ResultCode(int code, String msg) {
      this.code = code;
      this.msg = msg;
      }
      }

    3. 寫好列舉類,就開始寫 ResultVo 包裝類了,我們預設了幾種預設的方法,比如成功的話就預設傳入 object 就可以了,我們自動包裝成 success。

      @Data
      public classResultVo{
      // 狀態碼
      privateint code;
      // 狀態資訊
      private String msg;
      // 返回物件
      private Object data;
      // 手動設定返回vo
      publicResultVo(int code, String msg, Object data){
      this.code = code;
      this.msg = msg;
      this.data = data;
      }
      // 預設返回成功狀態碼,數據物件
      publicResultVo(Object data){
      this.code = ResultCode.SUCCESS.getCode();
      this.msg = ResultCode.SUCCESS.getMsg();
      this.data = data;
      }
      // 返回指定狀態碼,數據物件
      publicResultVo(StatusCode statusCode, Object data){
      this.code = statusCode.getCode();
      this.msg = statusCode.getMsg();
      this.data = data;
      }
      // 只返回狀態碼
      publicResultVo(StatusCode statusCode){
      this.code = statusCode.getCode();
      this.msg = statusCode.getMsg();
      this.data = null;
      }
      }




    4. 使用,現在的返回肯定就不是 return data;這麽簡單了,而是需要 new ResultVo(data);

      @PostMapping("/findByVo")
      public ResultVo findByVo(@Validated ProductInfoVo vo){
      ProductInfo productInfo = new ProductInfo();
      BeanUtils.copyProperties(vo, productInfo);
      returnnew ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
      }

    最後返回就會是上面帶了狀態碼的數據了。

    3 統一校驗

    原始做法

    假設有一個添加 ProductInfo 的介面,在沒有統一校驗時,我們需要這麽做。

    @Data
    public classProductInfoVo{
    // 商品名稱
    private String productName;
    // 商品價格
    private BigDecimal productPrice;
    // 上架狀態
    private Integer productStatus;
    }

    @PostMapping("/findByVo")
    public ProductInfo findByVo(ProductInfoVo vo){
    if (StringUtils.isNotBlank(vo.getProductName())) {
    thrownew APIException("商品名稱不能為空");
    }
    if (null != vo.getProductPrice() && vo.getProductPrice().compareTo(new BigDecimal(0)) < 0) {
    thrownew APIException("商品價格不能為負數");
    }
    ...
    ProductInfo productInfo = new ProductInfo();
    BeanUtils.copyProperties(vo, productInfo);
    returnnew ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
    }

    這 if 寫的人都傻了,能忍嗎?肯定不能忍啊。

    @Validated 參數校驗

    好在有 @Validated,又是一個校驗參數必備良藥了。有了 @Validated 我們只需要再 vo 上面加一點小小的註解,便可以完成校驗功能。

    @Data
    public classProductInfoVo{
    @NotNull(message = "商品名稱不允許為空")
    private String productName;
    @Min(value = 0, message = "商品價格不允許為負數")
    private BigDecimal productPrice;
    private Integer productStatus;
    }

    @PostMapping("/findByVo")
    public ProductInfo findByVo(@Validated ProductInfoVo vo){
    ProductInfo productInfo = new ProductInfo();
    BeanUtils.copyProperties(vo, productInfo);
    returnnew ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));
    }

    執行看看,如果參數不對會發生什麽?

    我們故意傳一個價格為 -1 的參數過去:

    productName : 泡腳
    productPrice : -1
    productStatus : 1

    {
    "timestamp""2020-04-19T03:06:37.268+0000",
    "status"400,
    "error""Bad Request",
    "errors": [
    {
    "codes": [
    "Min.productInfoVo.productPrice",
    "Min.productPrice",
    "Min.java.math.BigDecimal",
    "Min"
    ],
    "arguments": [
    {
    "codes": [
    "productInfoVo.productPrice",
    "productPrice"
    ],
    "defaultMessage""productPrice",
    "code""productPrice"
    },
    0
    ],
    "defaultMessage""商品價格不允許為負數",
    "objectName""productInfoVo",
    "field""productPrice",
    "rejectedValue": -1,
    "bindingFailure"false,
    "code""Min"
    }
    ],
    "message""Validation failed for object\u003d\u0027productInfoVo\u0027. Error count: 1",
    "trace""org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object \u0027productInfoVo\u0027 on field \u0027productPrice\u0027: rejected value [-1]; codes [Min.productInfoVo.productPrice,Min.productPrice,Min.java.math.BigDecimal,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [productInfoVo.productPrice,productPrice]; arguments []; default message [productPrice],0]; default message [商品價格不允許為負數]\n\tat org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:830)\n",
    "path""/leilema/product/product-info/findByVo"
    }

    大功告成了嗎?雖然成功校驗了參數,也返回了異常,並且帶上"商品價格不允許為負數"的資訊。

    但是你要是這樣返回給前端,前端妹妹就提刀過來了,當年約定好的狀態碼,你個負心人說忘就忘?

    使用者體驗小於等於 0 啊!所以我們要進行最佳化一下,每次出現異常的時候,自動把狀態碼寫好,不負妹妹之約!

    最佳化例外處理

    首先我們先看看校驗參數丟擲了什麽異常:

    Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors

    我們看到程式碼丟擲了 org.springframework.validation.BindException 的繫結異常,因此我們的思路就是 AOP 攔截所有 controller,然後異常的時候統一攔截起來,進行封裝!完美!

    玩你個頭啊完美,這麽呆瓜的操作 springboot 不知道嗎?spring mvc 當然知道拉,所以給我們提供了一個 @RestControllerAdvice 來增強所有 @RestController,然後使用 @ExceptionHandler 註解,就可以攔截到對應的異常。

    這裏我們就攔截 BindException. class 就好了。最後在返回之前,我們對異常資訊進行包裝一下,包裝成 ResultVo,當然要跟上 ResultCode.VALIDATE_ERROR 的異常狀態碼。

    這樣前端妹妹看到 VALIDATE_ERROR 的狀態碼,就會呼叫數據校驗異常的彈窗提示使用者哪裏沒填好。

    @RestControllerAdvice
    public classControllerExceptionAdvice{
    @ExceptionHandler({BindException. class})
    publicResultVoMethodArgumentNotValidExceptionHandler(BindExceptione
    {
    // 從異常物件中拿到ObjectError物件
    ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
    returnnew ResultVo(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage());
    }
    }

    來看看效果,完美。1002 與前端妹妹約定好的狀態碼:

    {
    "code"1002,
    "msg""參數校驗失敗",
    "data""商品價格不允許為負數"
    }

    4 統一響應

    統一包裝響應

    再回頭看一下 controller 層的返回:

    returnnew ResultVo(productInfoService.getOne(new QueryWrapper(productInfo)));

    開發小哥肯定不樂意了,誰有空天天寫 new ResultVo(data) 啊,我就想返回一個實體!怎麽實作我不管!

    好把,那就是 AOP 攔截所有 Controller,再 @After 的時候統一幫你封裝一下咯。

    怕是上一次臉打的不夠疼,springboot 能不知道這麽個操作嗎?

    @RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
    public classControllerResponseAdviceimplementsResponseBodyAdvice<Object{
    @Override
    publicbooleansupports(MethodParameter methodParameter, class<? extends HttpMessageConverter<?>> a class){
    // response是ResultVo型別,或者註釋了NotControllerResponseAdvice都不進行包裝
    return !methodParameter.getParameterType().isAssignableFrom(ResultVo. class);
    }
    @Override
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, class<? extends HttpMessageConverter<?>> a class, ServerHttpRequest request, ServerHttpResponse response){
    // String型別不能直接包裝
    if (returnType.getGenericParameterType().equals(String. class)) {
    ObjectMapper objectMapper = new ObjectMapper();
    try {
    // 將封包裝在ResultVo裏後轉換為json串進行返回
    return objectMapper.writeValueAsString(new ResultVo(data));
    catch (JsonProcessingException e) {
    thrownew APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());
    }
    }
    // 否則直接包裝成ResultVo返回
    returnnew ResultVo(data);
    }
    }

    1. @RestControllerAdvice(basePackages = {"com.bugpool.leilema"}) 自動掃描了所有指定包下的 controller,在 Response 時進行統一處理。

    2. 重寫 supports 方法,也就是說,當返回型別已經是 ResultVo 了,那就不需要封裝了,當不等與 ResultVo 時才進行呼叫 beforeBodyWrite 方法,跟過濾器的效果是一樣的。

    3. 最後重寫我們的封裝方法 beforeBodyWrite,註意除了 String 的返回值有點特殊,無法直接封裝成 json,我們需要進行特殊處理,其他的直接 new ResultVo(data); 就 ok 了。

    打完收工,看看效果:

    @PostMapping("/findByVo")
    public ProductInfo findByVo(@Validated ProductInfoVo vo){
    ProductInfo productInfo = new ProductInfo();
    BeanUtils.copyProperties(vo, productInfo);
    return productInfoService.getOne(new QueryWrapper(productInfo));
    }

    此時就算我們返回的是 po,接收到的返回就是標準格式了,開發小哥露出了欣慰的笑容。

    {
    "code"1000,
    "msg""請求成功",
    "data": {
    "productId"1,
    "productName""泡腳",
    "productPrice"100.00,
    "productDescription""中藥泡腳加按摩",
    "productStatus"0,
    ...
    }
    }

    NOT 統一響應

    不開啟統一響應原因: 開發小哥是開心了,可是其他系統就不開心了。舉個例子:我們計畫中整合了一個健康檢測的功能,也就是這貨。

    @RestController
    public classHealthController{
    @GetMapping("/health")
    public String health(){
    return"success";
    }
    }

    公司部署了一套校驗所有系統存活狀態的工具,這工具就定時發送 get 請求給我們系統:

    「兄弟,你死了嗎?」 「我沒死,滾」 「兄弟,你死了嗎?」 「我沒死,滾」

    是的,web 計畫的本質就是復讀機。一旦發送的請求沒響應,就會給負責人發資訊(企業微信或者簡訊之類的),你的系統死啦!趕緊回來排查 bug 吧!

    讓大家感受一下。每次看到我都射射發抖,早上 6 點!我 tm!!!!!

    好吧,沒辦法,人家是老大,人家要的返回不是:

    {
    "code"1000,
    "msg""請求成功",
    "data""success"
    }

    人家要的返回只要一個 success,人家定的標準不可能因為你一個系統改。俗話說的好,如果你改變不了環境,那你就只能我****

    新增不進行封裝註解: 因為百分之 99 的請求還是需要包裝的,只有個別不需要,寫在包裝的過濾器吧?又不是很好維護,那就加個註解好了。所有不需要包裝的就加上這個註解。

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public@interface NotControllerResponseAdvice {
    }

    然後在我們的增強過濾方法上過濾包含這個註解的方法:

    @RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
    public classControllerResponseAdviceimplementsResponseBodyAdvice<Object{
    @Override
    publicbooleansupports(MethodParameter methodParameter, class<? extends HttpMessageConverter<?>> a class){
    // response是ResultVo型別,或者註釋了NotControllerResponseAdvice都不進行包裝
    return !(methodParameter.getParameterType().isAssignableFrom(ResultVo. class)
    || methodParameter.hasMethodAnnotation(NotControllerResponseAdvice. class))
    ;
    }
    ...

    最後就在不需要包裝的方法上加上註解:

    @RestController
    public classHealthController{
    @GetMapping("/health")
    @NotControllerResponseAdvice
    public String health(){
    return"success";
    }
    }

    這時候就不會自動封裝了,而其他沒加註解的則依舊自動包裝:

    5 統一異常

    每個系統都會有自己的業務異常,比如庫存不能小於 0 子類別的,這種異常並非程式異常,而是業務操作引發的異常,我們也需要進行規範的編排業務異常狀態碼,並且寫一個專門處理的異常類,最後透過剛剛學習過的異常攔截統一進行處理,以及打日誌。

    1. 異常狀態碼列舉,既然是狀態碼,那就肯定要實作我們的標準介面 StatusCode。

      @Getter
      publicenum AppCode implements StatusCode {
      APP_ERROR(2000"業務異常"),
      PRICE_ERROR(2001"價格異常");
      privateint code;
      private String msg;
      AppCode(int code, String msg) {
      this.code = code;
      this.msg = msg;
      }
      }

    2. 異常類,這裏需要強調一下,code 代表 AppCode 的異常狀態碼,也就是 2000;msg 代表業務異常,這只是一個大類,一般前端會放到彈窗 title 上;最後 super(message); 這才是丟擲的詳細資訊,在前端顯示在彈表單中,在 ResultVo 則保存在 data 中。

      @Getter
      public classAPIExceptionextendsRuntimeException{
      privateint code;
      private String msg;
      // 手動設定異常
      publicAPIException(StatusCode statusCode, String message){
      // message用於使用者設定丟擲錯誤詳情,例如:當前價格-5,小於0
      super(message);
      // 狀態碼
      this.code = statusCode.getCode();
      // 狀態碼配套的msg
      this.msg = statusCode.getMsg();
      }
      // 預設異常使用APP_ERROR狀態碼
      publicAPIException(String message){
      super(message);
      this.code = AppCode.APP_ERROR.getCode();
      this.msg = AppCode.APP_ERROR.getMsg();
      }
      }

    3. 最後進行統一異常的攔截,這樣無論在 service 層還是 controller 層,開發人員只管丟擲 API 異常,不需要關系怎麽返回給前端,更不需要關心日誌的打印。

      @RestControllerAdvice
      public classControllerExceptionAdvice{
      @ExceptionHandler({BindException. class})
      publicResultVoMethodArgumentNotValidExceptionHandler(BindExceptione
      {
      // 從異常物件中拿到ObjectError物件
      ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
      returnnew ResultVo(ResultCode.VALIDATE_ERROR, objectError.getDefaultMessage());
      }
      @ExceptionHandler(APIException. class)
      publicResultVoAPIExceptionHandler(APIExceptione
      {
      // log.error(e.getMessage(), e); 由於還沒整合日誌框架,暫且放著,寫上TODO
      returnnew ResultVo(e.getCode(), e.getMsg(), e.getMessage());
      }
      }

    4. 最後使用,我們的程式碼只需要這麽寫。

      if (null == orderMaster) {
      thrownew APIException(AppCode.ORDER_NOT_EXIST, "訂單號不存在:" + orderId);
      }

      效果:

      {
      "code"2003,
      "msg""訂單不存在",
      "data""訂單號不存在:1998"
      }

    就會自動丟擲 AppCode.ORDER_NOT_EXIST 狀態碼的響應,並且帶上異常詳細資訊訂單號不存在:xxxx。

    後端小哥開發有效率,前端妹妹獲取到 2003 狀態碼,呼叫對應警告彈窗,title 寫上訂單不存在,body 詳細資訊記載"訂單號不存在:1998"。同時日誌還自動打上去了!

    鐵子們!覺得不錯,給個三連吧!

    來源:bugpool.blog.csdn.net/

    article/details/105610962

    >>

    END

    精品資料,超贊福利,免費領

    微信掃碼/長按辨識 添加【技術交流群

    群內每天分享精品學習資料

    最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎並行JVMMySQLRedisSpringSpringMVCSpringBootSpringCloud訊息佇列等多個型別),歡迎您的使用。

    👇👇

    👇點選"閱讀原文",獲取更多資料(持續更新中