精進代碼 - 接口統一響應體

前言

  如今web項目的接口大都是 RESTful 的,響應體中包含了一些接口執行的信息,比如返回的數據(data)、響應碼(code)、是否成功(success)和響應描述(message)。每個接口都需要封裝成這種格式,這樣每次都需要留意。
  下面來介紹一種方法,是 Spring 的 web 模塊提供的功能。

統一響應體

pom依賴了web模塊和lombok

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.61</version>
</dependency>

首先創建枚舉類ResultMessage,定義一些響應碼

public enum  ResultMessage {
  /** 成功 */
  SUCCESS(200, "Success"),
  /** 請求錯誤 */
  BAD_REQUEST(400, "Bad request"),
  /** 未經授權 */
  UNAUTHORIZED(401, "Unauthorized"),
  /** 目標不存在 */
  NOT_FOUND(404, "Not found"),
  /** 服務內部錯誤 */
  SERVER_ERROR(500, "Server Error");

  private int code;
  private String message;

  ResultMessage(int code, String message) {
    this.code = code;
    this.message = message;
  }

  public int code() {
    return code;
  }

  public String message() {
    return message;
  }
}

創建 Result 類,代表接口的響應體

@Getter
public class Result<T> {

  /**
   * 狀態碼
   */
  private int code = -1;
  /**
   * 提示信息
   */
  private String message;
  /**
   * 響應數據
   */
  private T data;

  private Result(int code, String message, T data) {
    this.code = code;
    this.message = message;
    this.data = data;
  }

  public static <T> Result<T> data(T data) {
    return new Result<>(ResultMessage.SUCCESS.code(), ResultMessage.SUCCESS.message(), data);
  }

  public static <T> Result<T> err() {
    return new Result<>(ResultMessage.SERVER_ERROR.code(), ResultMessage.SERVER_ERROR.message(), null);
  }

  public static <T> Result<T> err(String message) {
    return new Result<>(ResultMessage.SERVER_ERROR.code(), message, null);
  }
}

接着創建,HelloController類

@RestController
public class HelloController {

  @GetMapping("/hello")
  public Result<String> hello() {
    return Result.data("hello");
  }
}

使用postman訪問 http://localhost:8080/hello

{
    "code": 200,
    "message": "Success",
    "data": "hello"
}

創建註解 OriginalBody,代表原始響應頭。

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

創建 ResultBodyAdvice 類,實現 ResponseBodyAdvice 接口,重寫兩個方法:

@ControllerAdvice
public class ResultBodyAdvice implements ResponseBodyAdvice {
  @Override
  public boolean supports(MethodParameter methodParameter, Class aClass) {
    Annotation[] methodAnnotations = methodParameter.getMethodAnnotations();
    for (Annotation methodAnnotation : methodAnnotations) {
      if (methodAnnotation instanceof OriginalBody) {
        return false;
      }
    }
    Annotation[] classAnnotations = methodParameter.getMethod().getDeclaringClass().getAnnotations();
    for (Annotation classAnnotation : classAnnotations) {
      if (classAnnotation instanceof OriginalBody) {
        return false;
      }
    }
    return true;
  }

  @Override
  public Object beforeBodyWrite(Object result, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse response) {
    if (result instanceof Result) {
      return result;
    }
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
    String type = mediaType.getType();
    if ("text".equals(type)) {
      return JSON.toJSONString(Result.data(result), SerializerFeature.PrettyFormat);
    }
    return Result.data(result);
  }
}

在 HelloController 新增兩個方法,用來測試

@GetMapping("/hello2")
public String hello2() {
	return "hello2";
}

@OriginalBody
@GetMapping("/hello3")
public String hello3() {
	return "hello3";
}

使用Postman分別調用 hello2:

{
	"code": 200,
	"data": "hello2",
	"message": "Success"
}

調用 hello3 :

hello3

統一異常響應體

新增一個方法,如果接口出現異常,則響應成了醬紫:

@GetMapping("/hello4")
public String hello4(@RequestParam Integer code) {
  if (code != null && code.equals(1)) {
    throw new RuntimeException();
  }
  return "hello4";
}
{
    "code": 200,
    "message": "Success",
    "data": {
        "timestamp": "2020-04-10T11:06:45.299+0000",
        "status": 500,
        "error": "Internal Server Error",
        "message": "No message available",
        "path": "/hello4"
    }
}

這種方法使用 @ExceptionHandler 註解來解決。
創建

@ControllerAdvice
@Slf4j
@ResponseBody
public class ExceptionHandlerAdvice {
  @ExceptionHandler(Exception.class)
  public Result<Object> handleException(Exception e){
    log.error(e.getMessage(), e);
    return Result.err();
  }
}

請求結果:

{
    "code": 500,
    "message": "Server Error",
    "data": null
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章