成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

別錯過!Spring Boot 實現統一響應結果的五種方案

開發 前端
在 Spring Boot 項目開發中,隨著接口數量增多,不同接口的響應結果格式各異,這給前端開發和接口維護帶來諸多不便。

環境:SpringBoot3.4.2

1. 簡介

在 Spring Boot 項目開發中,隨著接口數量增多,不同接口的響應結果格式各異,這給前端開發和接口維護帶來諸多不便。前端需要針對不同格式做適配,增加開發成本;后端維護時,也易因格式混亂導致錯誤。

統一響應結果能解決這些問題。它規定所有接口返回相同格式的數據,如包含狀態碼、消息、數據等字段。例如,成功時返回狀態碼 200、消息“操作成功”和數據;失敗時返回對應錯誤碼和消息。這樣前端只需按統一格式解析,降低開發復雜度;后端維護也更便捷,只需關注業務邏輯。統一響應結果提升了代碼可讀性、可維護性,增強了前后端協作效率。

在本篇文章里,我們將針對 Spring Boot 中實現統一響應結果,詳細闡述 6 種切實可行的方案。

2.實戰案例

2.1 自定義響應實體類

定義一個通用的響應類,包含狀態碼、消息和數據。然后在Controller中返回這個類的實例。

自定義狀態碼

public enum ResultCode {
  SUCCESS(200, "操作成功"), FAILURE(400, "業務異常"), 
  UNAUTHORIZED(401, "未授權"), FORBIDDEN(403, "禁止訪問"), 
  NOT_FOUND(404, "資源不存在"), INTERNAL_ERROR(500, "系統錯誤") ;
  private final int code ;
  private final String msg ;
  // getters
}

自定義結果對象

public class ApiResponse<T> {
  private int code ;
  private String message ;
  private T data;
  public ApiResponse(int code, String message, T data) {
    this.code = code;
    this.message = message;
    this.data = data;
  }
  // 成功響應(帶數據)
  public static <T> ApiResponse<T> success(T data) {
    return new ApiResponse<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data);
  }
  // 失敗響應
  public static <T> ApiResponse<T> fail(ResultCode resultCode) {
    return new ApiResponse<>(resultCode.getCode(), resultCode.getMsg(), null);
  }
  // ...
  // getters, setters
}

Controller接口

@RestController
@RequestMapping("/way1")
public class Way1Controller {
  @GetMapping("/{id}")
  public ApiResponse<User> getUser(@PathVariable Long id) {
    return ApiResponse.success(new User(id, "Pack")) ;
  }
  // 錯誤處理
  @ExceptionHandler(Exception.class)
  public ApiResponse<Void> handleException(Exception e) {
    return ApiResponse.fail(ResultCode.NOT_FOUND);
  }
}

2.2 自定義ResponseBodyAdvice

通過實現 ResponseBodyAdvice 接口,可以對Controller返回的結果進行統一封裝。這種方式不需要修改每個Controller方法。

自定義注解(標注那些不需要處理的接口)

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

自定義ResponseBodyAdvice

@RestControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {
  private final ObjectMapper objectMapper ;
  public GlobalResponseAdvice(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }
  @Override
  public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return !returnType.hasMethodAnnotation(NoWrap.class);
  }
  @Override
  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
      Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
      ServerHttpResponse response) {
    // 處理String類型特殊轉換
    if (body instanceof String) {
      try {
        return this.objectMapper.writeValueAsString(ApiResponse.success(body)) ;
      } catch (JsonProcessingException e) {
        System.err.printf("JSON序列化錯誤: %s%n", e.getMessage()) ;
        return body ;
      }
    }
    // 已封裝過的響應直接返回
    if (body instanceof ApiResponse) {
      return body ;
    }
    // 空響應
    if (body == null && returnType.getParameterType().equals(void.class)) {
      return ApiResponse.success();
    }
    return ApiResponse.success(body) ;
  }
}

Controller接口

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
  return new User(id, "Pack") ;
}
@GetMapping("/query")
public String query() {
  return "查詢完成" ;
}

圖片圖片

圖片圖片

2.3 使用AOP技術

我們也可以使用AOP來攔截Controller方法的返回值,然后進行統一封裝。但是AOP技術的局限性非常大,我們首先要統一Controller接口的響應類型比如:使用ResponseEntity 作為統一方法返回類型。

@Aspect
@Component
public class ResponseAspect {
  @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
  public void controllerPointcut() {
  }
  @Around("controllerPointcut()")
  public Object handleResponse(ProceedingJoinPoint pjp) throws Throwable {
    Object result = pjp.proceed();
    if (result instanceof ResponseEntity<?> ret) {
      // 對ResponseEntity的body進行統一處理
      Object body = ApiResponse.success(ret.getBody()) ;
      return ResponseEntity.ok(body) ;
    }
    return result;
  }
}

Controller接口

@GetMapping("/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
  return ResponseEntity.ok(new User(id, "Pack")) ;
}

圖片

此種方式最不推薦的了,Controller接口都已經統一了ResponseEntity,那么我還搞個AOP做什么?

2.4 使用Filter

此種方式可行,但是也不推薦,復雜且易出錯,破壞流式響應。

@WebFilter("/way4/*")
public class ResponseContentFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
      throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) resp;
    ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
    try {
      chain.doFilter(req, responseWrapper);
    } finally {
      // ...
    }
    String content = new String(responseWrapper.getContentAsByteArray(), "UTF-8") ;
    ObjectMapper mapper = new ObjectMapper();
    byte[] ret = mapper.writeValueAsBytes(ApiResponse.success(content)) ;
    response.getOutputStream().write(ret) ;
  }
}

Controller接口

@GetMapping("/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
  return ResponseEntity.ok(new User(id, "Pack")) ;
}

圖片圖片

通過Filter方式可能對那些 遺留系統改造 有用吧。

2.5 自定義HttpMessageConverter

通過自定義HttpMessageConverter方法提供了對響應處理過程的精細控制,特別適合需要完全定制響應格式的場景。

@Component
public class ResponseMessageConverter extends AbstractHttpMessageConverter<Object> {
  private final ObjectMapper objectMapper;
  public ResponseMessageConverter(ObjectMapper objectMapper) {
    super(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML) ;
    this.objectMapper = objectMapper;
  }
  @Override
  protected boolean supports(Class<?> clazz) {
    // 支持所有類型,除了我們自己的ApiResponse和Void
    return !ApiResponse.class.isAssignableFrom(clazz) && !Void.TYPE.equals(clazz);
  }
  @Override
  protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
      throws IOException, HttpMessageNotReadableException {
    // 只處理輸出,不處理輸入
    return null;
  }
  @Override
  protected void writeInternal(Object body, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException {
    // 由于我們上面配置的能夠支持text/html,所以必須在這里配置Content-Type否則統一都會通過text/html響應這會出現亂碼問題
    outputMessage.getHeaders().add("Content-Type", "application/json;charset=utf-8");
    // 創建統一響應體
    ApiResponse<Object> response = ApiResponse.success(body);
    try {
      // 序列化響應體
      String json = objectMapper.writeValueAsString(response);
      // 寫入響應
      outputMessage.getBody().write(json.getBytes(StandardCharsets.UTF_8));
    } catch (JsonProcessingException e) {
      throw new HttpMessageNotWritableException("Error writing response", e);
    }
  }
}

Controller接口

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
  return new User(id, "Pack") ;
}
@GetMapping("/query")
public String query() {
  return "查詢完成" ;
}

圖片圖片

圖片圖片

總結

該方案核心優勢在于細粒度控制與高性能:

  • 直接操作輸出流,性能最優
  • 精準控制封裝邏輯,通過supports()方法實現條件過濾
  • 完全掌控響應結構,支持特殊類型(如void/String)
  • 與Spring MVC原生機制無縫集成,不破壞現有流程
  • 避免全局包裝的過度處理,保持框架靈活性

適用于需要極致性能優化和深度定制響應格式的高要求場景。

責任編輯:武曉燕 來源: Springboot全家桶實戰案例
相關推薦

2024-08-01 09:10:03

2024-09-25 08:10:00

Spring后端

2025-06-06 08:28:56

2023-11-30 07:00:56

SpringBoot處理器

2025-03-31 08:39:55

2024-09-29 09:31:08

Spring前綴URL

2022-08-18 09:38:02

Spring跨域

2025-06-12 02:00:00

Spring簽到打卡

2025-02-13 00:34:22

Spring對象系統

2024-08-29 09:01:39

2025-06-17 07:37:53

2025-02-13 07:45:26

APISpringHTTP

2025-02-12 08:47:07

SpringAPI接口

2023-11-28 14:32:04

2025-05-30 08:11:30

2025-01-09 08:36:05

2025-05-14 04:00:00

2025-04-30 08:39:33

SpringMVC接口

2025-06-04 02:10:00

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品久久久久久久久 | 久久乐国产精品 | 一区视频| 亚洲国产成人久久综合一区,久久久国产99 | 欧美一区二区三区 | 中文字幕97 | 日本不卡一区二区三区在线观看 | 91久久精品 | 美女国内精品自产拍在线播放 | 国产资源一区二区三区 | 久久久久久久一级 | 日本在线免费观看 | 精区3d动漫一品二品精区 | 日韩欧美一级片 | 国产伦精品一区二区三区照片91 | 国产精品久久久久久久久久免费看 | 久久伊人精品 | 91视频网 | 福利一区在线观看 | 中文字幕乱码亚洲精品一区 | 久久久久国色av免费观看性色 | 色.com| 亚洲社区在线 | 亚洲福利网站 | 日韩中文一区二区三区 | 久久精品成人 | 欧美男人天堂 | 黄色三级毛片 | 综合精品| 欧美在线视频一区二区 | 精品久久国产 | 自拍偷拍中文字幕 | 色综合色综合色综合 | 91精品国产91久久久久久吃药 | 天天操天天射综合网 | 国产99久久久国产精品 | 欧美日本一区 | 一区二区视频在线 | 一区二区视频在线 | 国产成人精品久久二区二区 | 99re视频 |