跳转至

自定義 Spring Boot 預設返回的 http 狀態碼

ResponseEntity?

用法: 作為方法的返回類型

用途: 自定義回傳的http response 的細節

預設是 200 OK

在下面自定義後 ,就變成了 200 ACCEPTED

@RestController
public class MyController5 {
    @RequestMapping("/test")
    public ResponseEntity<String> test() {
        return ResponseEntity.status(HttpStatus.ACCEPTED).body("Hellow World");
    }
}

@ControllerAdvice + @ExceptionHandler

定義每個Exception 所返回的 http response

@ControllerAdvice

用法: 只能加在 class 上

用途: 將這個class 變成一個bean ,並且可以在內部使用 @ExceptionHandler

@ExceptionHandler

用法: 只能加在 方法上

用途: 去 catch 方法所噴出的 Exception

Throwable 底下

Error 系統錯誤 ,不需處理
Exception 程式錯誤 ,需要處理
digraph hierarchy {

nodesep=1.0 // increases the separation between nodes

node [color=Black,fontname=Courier,shape=box] //All nodes will this shape and colour
edge [color=Red, style=dashed] //All the lines look like this

Throwable->{Error Exception}
Error->{OutOFMemoryError }
Exception->{RuntimeException IOException}
RuntimeException->{IllegalArgumentException }
}

練習

@RestController
public class MyController6 {
    @RequestMapping("/test")
    public String test() {
        throw new RuntimeException("test error");
    }
}

自定義 RuntimeException 所返回的 http response

package com.example.demo;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class MyExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<String> handle(RuntimeException exception){
        /* 當 Controller 裡面的任何一個方法去噴出 RuntimeException 的時候
         * 這個 @ExceptionHandler 他就會將 這個 RuntimeException 給接住
         * 所以可以在這個 handle() 方法中去定義說要返回的 http response 給前端
         */
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("RuntimeException: " + exception.getMessage());
    }
}

Talend API Tester

GET http://localhost:8080/test

練習2

@RestController
public class MyController6 {
    @RequestMapping("/test")
    public String test() {
        throw new RuntimeException("test error");
    }

    @RequestMapping("/test2")
    public String test2() {
        throw new IllegalArgumentException("test2 error");
    }
}
package com.example.demo;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class MyExceptionHandler {
        @ExceptionHandler(RuntimeException.class)
        public ResponseEntity<String> handle(RuntimeException exception){
            return ResponseEntity
                    .status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body("RuntimeException: " + exception.getMessage());
        }

        @ExceptionHandler(IllegalArgumentException.class)
        public ResponseEntity<String> handle(IllegalArgumentException exception){
            return ResponseEntity
                    .status(HttpStatus.BAD_REQUEST)
                    .body("IllegalArgumentException: " + exception.getMessage())
        }
}

Talend API Tester

GET http://localhost:8080/test2

如果沒有用 @ExceptionHandler 對 throw new IllegalArgumentException

則Spring Boot 會透過繼承關係 ,去找IllegalArgumentException 所繼承的class ,找到對應的 @ExceptionHandler

@RestController
public class MyController6 {
    @RequestMapping("/test")
    public String test() {
        throw new RuntimeException("test error");
    }

    @RequestMapping("/test2")
    public String test2() {
        throw new IllegalArgumentException("test2 error");
    }
}
把 IllegalArgumentException 拿掉後 ,只留下 RuntimeException

package com.example.demo;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class MyExceptionHandler {
        @ExceptionHandler(RuntimeException.class)
        public ResponseEntity<String> handle(RuntimeException exception){
            return ResponseEntity
                    .status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body("RuntimeException: " + exception.getMessage());
        }
}

由此可以看出 public class IllegalArgumentException extends RuntimeException

他跳503 ,是因為上一層是 RuntimeException

使用 @ControllerAdvice 的好處: 統一管理 Exception

可以不用一直重複寫 try…catch

@RestController
public class MyController6 {
    @RequestMapping("/test")
    public String test() {
        try{                
            throw new RuntimeException("test error");       
        }catch(RuntimeException e){
            return e.getMessage();
        }
    }
}

底層由 Spring AOP 所實作

@ControllerAdvice 可以想像成 專門在處理大家所噴出的 Exception 的切面