第三方API响应码处理最佳实践:以天气API对接场景为例
最优方案与代码组织
这个场景下自定义异常+全局异常映射是最优的组织方式,也是业内对接第三方API的通用实践,比返回带状态的实体更合理,核心优势是业务逻辑与错误处理完全解耦,代码可读性和可扩展性更高。
你当前用的是JAX-RS标准的接口定义(@Path、Response),这套方案完美适配现有技术栈,不需要大改原有代码:
- 先定义一组自定义异常,对应不同的错误场景,也可以封装一个通用父异常承载HTTP状态码,减少重复代码
- 在
weatherService对接第三方API的逻辑中,解析第三方返回的状态码,错误场景直接抛出对应的自定义异常 - 用JAX-RS的
ExceptionMapper或者Spring生态的@RestControllerAdvice做全局异常统一处理,直接转换成对应状态码的响应
两种方案对比
自定义异常(推荐)
- 适用场景:当前这种第三方调用错误属于非预期的异常分支,符合Java异常机制的设计初衷
- 优势:业务接口代码完全不需要加错误判断的if-else,错误处理逻辑统一收口,后续要加错误日志、监控告警只需要在异常处理器里修改即可,不需要侵入业务代码
- 符合防腐层(Anticorruption Layer)的设计思想,把第三方API的错误模型和你的系统逻辑完全隔离开,避免第三方的接口定义渗透到上层业务
带状态的返回实体
- 适用场景:如果错误属于业务正常流程的一部分(比如用户操作类的业务错误,需要前端做特殊跳转/提示),可以用返回实体承载状态
- 劣势:当前场景下会导致所有调用
weatherService的地方都需要加状态判断的冗余代码,后续新增错误类型需要修改所有调用方,维护成本高
可使用的工具库
- 框架原生能力:JAX-RS的
ExceptionMapper(Resteasy、Jersey等实现都原生支持)、Spring Boot的@RestControllerAdvice,不需要额外引入依赖就能实现全局异常处理 - 简化开发:用Lombok注解减少自定义异常的样板代码
- 容错增强:如果要实现第三方调用失败自动重试、熔断降级,可以用Resilience4j,针对第三方500错误配置重试策略,重试失败再抛出异常返回503,能大幅提升接口可用性
代码示例
1. 自定义异常定义
// 通用第三方调用父异常 public class ThirdPartyApiException extends RuntimeException { private final int httpStatus; public ThirdPartyApiException(String message, int httpStatus) { super(message); this.httpStatus = httpStatus; } public int getHttpStatus() { return httpStatus; } } // 具体场景异常 public class InvalidCoordinateException extends ThirdPartyApiException { public InvalidCoordinateException(String message) { super(message, 400); } } public class LocationNotCoveredException extends ThirdPartyApiException { public LocationNotCoveredException(String message) { super(message, 404); } } public class WeatherServiceUnavailableException extends ThirdPartyApiException { public WeatherServiceUnavailableException(String message) { super(message, 503); } }
2. weatherService实现
public Weather getWeather(double latitude, double longitude) { // 调用第三方天气API的逻辑 Response thirdPartyResp = thirdPartyWeatherClient.getWeather(latitude, longitude); int status = thirdPartyResp.getStatus(); if (status == 200) { return thirdPartyResp.readEntity(Weather.class); } else if (status == 400) { throw new InvalidCoordinateException("经纬度参数非法"); } else if (status == 404) { throw new LocationNotCoveredException("请求位置不在服务覆盖范围内"); } else if (status == 500) { throw new WeatherServiceUnavailableException("天气服务暂时不可用"); } // 未知错误统一返回503 throw new WeatherServiceUnavailableException("天气服务调用失败"); }
3. 全局异常处理器(JAX-RS版本)
@Provider public class ThirdPartyApiExceptionMapper implements ExceptionMapper<ThirdPartyApiException> { @Override public Response toResponse(ThirdPartyApiException e) { // 可以封装统一的错误响应体 ErrorResponse error = new ErrorResponse(e.getMessage()); return Response.status(e.getHttpStatus()).entity(error).build(); } }
4. 原有接口代码完全不需要修改
@Path("/weather") Response getWeather(double latitude, double longtitude) { Weather weather = weatherService.getWeather(latitude, longtitude); return wrapWithHttpOK(weather); }
内容的提问来源于stack exchange,提问作者Ivan Terekh
相关产品推荐
相关产品推荐

