接口响应时组装响应json_企业项目之spring项目《接口响应体格式统一封装》

f7575623d2fa98400b4b701ba3876b6b.png

前言

如何更好、更简单的写好一个接口(接口返回值篇),今天的这篇文章我们主要介绍,怎么统一处理下接口的返回格式问题。

我们先来分析下我们所面临的问题在哪里,然后接着给出解决方案。在写一个接口时,我们通常会先统一定义一下接口的返回格式是什么,然后再跟前端去对接,接口响应值格式为:

1.默认的成功响应数据格式:

{"id": 322,"userId": 232,"eventName": "testEvent","eventData": "testData","create_time": "2020-08-05T14:45:50.691+00:00","update_time": "2020-08-05T14:45:50.691+00:00"}

2.失败的响应格式

Whitelabel Error PageThis application has no explicit mapping for /error, so you are seeing this as a fallback.Wed Aug 05 22:47:02 CST 2020There was an unexpected error (type=Internal Server Error, status=500).

响应没有一个固定格式会造成调试十分困难,所以需要做的是

1.将成功的响应格式变为固定格式

2.拦截异常请求,封装为固定的格式,用响应码的方式将错误传递给前端

1.修改后的代码对应成功的响应数据格式

{"code": "SUCCESS","message": "ok","subCode": null,"subMessage": null,"data": {"id": 322,"userId": 232,"eventName": "testEvent","eventData": "testData","create_time": "2020-08-05T14:41:05.652+00:00","update_time": "2020-08-05T14:41:05.652+00:00"}}

2.修改后的代码对应错误的响应数据格式

{"code": "SYSTEM_ERROR","message": "出错了","subCode": null,"subMessage": null,"data": null}

那应该如何修改代码才能满足第二种格式呢:

接口用法

@GetMapping(value = "/testUser", produces = "application/json")    public IHttpResult getUserInfo() throws Exception{        UserAction userAction = new UserAction().setId(322).setCreate_time(new Date())                .setEventData("testData").setEventName("testEvent").setUpdate_time(new Date()).setUserId(232);        return DefaultHttpPageResult.successWithData(userAction);    }

异常代码处理类

@SuppressWarnings("rawtypes")@ExceptionHandler(Exception.class)public @ResponseBody    Object exceptionHandler(Exception exception) {DefaultHttpResult httpResult = new DefaultHttpResult();if (exception instanceof MethodArgumentNotValidException) {BindingResult bindingResult = ((MethodArgumentNotValidException) exception).getBindingResult();StringBuilder strBuilder = new StringBuilder();for (FieldError fieldError : bindingResult.getFieldErrors()) {strBuilder.append(fieldError.getDefaultMessage()).append("/");}String msg = strBuilder.toString();logger.error("捕获到参数校验异常:{}, exception:", msg, exception);httpResult.setCode(PlatformCode.PARAM_ERROR.getCode());httpResult.setMessage(msg);} else {logger.error("捕获到系统错误:{}, exception:", exception.getMessage(), exception);httpResult.setCode(PlatformCode.SYSTEM_ERROR.getCode());httpResult.setMessage(exception.getMessage());}return httpResult;}

但是上述代码发现DefaultHttpPageResult.successWithData()这段逻辑显然很多余,每个方法都要这样写一遍,所以上述方式并不是我们想要的,我们要的是

@GetMapping(value = "/testSimpleUser", produces = "application/json")    public UserAction getSimpleUser() throws Exception{        UserAction userAction = new UserAction().setId(322).setCreate_time(new Date())                .setEventData("testData").setEventName("testEvent").setUpdate_time(new Date()).setUserId(232);        return userAction;    }

我们加一个@ControllerAdvice(basePackages = "com.global.format.controller")注解,全局拦截Controller类下的所有方法,对所有返回值进行处理即可避免每个方法写上面多余的代码,代码如下:

@ControllerAdvice(basePackages = "com.global.format.controller")public class GlobalResponseBodyAdvice implements ResponseBodyAdvice {private static final Logger logger = LoggerFactory.getLogger(GlobalResponseBodyAdvice.class);//可以使用白名单排查有些不组装结果的接口,如监控,监控检查等接口private static final List WHITE_LIST_PATH = Arrays.asList( "/test/common" );@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SuppressWarnings({ "unchecked" })@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {String requestPath = request.getURI().getPath();if (WHITE_LIST_PATH.contains(requestPath)) {logger.info("GDX-DEBUG request path:{} in response body advice white list.", requestPath);return body;}// 返回值空值处理if (body == null) {DefaultHttpResult httpResult = new DefaultHttpResult();httpResult.setCode(PlatformCode.SUCCESS.getCode());httpResult.setMessage(PlatformCode.SUCCESS.getMessage());httpResult.setData(null);return httpResult;}// 兼容Rpc返回值规范if (body instanceof IPageResp) {IPageResp pageResp = (IPageResp) body;DefaultHttpResult httpResult = new DefaultHttpResult();httpResult.setCode(PlatformCode.SUCCESS.getCode());httpResult.setMessage(PlatformCode.SUCCESS.getMessage());httpResult.setData(DefaultPageResp.buildPageResp(pageResp.getPageNum(), pageResp.getPageSize(),pageResp.getTotalCount(), pageResp.getPageRecords()));return httpResult;} // 兼容之前APi规范老代码else if (body instanceof IHttpResult) {return body;} else {// 将新返回值统一封装到api规范中IHttpResult objectIHttpResult = DefaultHttpResult.successWithData(body);if (body instanceof String) {// String需要特殊处理,SpringMVC会根据返回接口参数为对象时将当前返回值当成String处理,会出现强转异常try {//jsonManager对象可以处理为单例return new ObjectMapper().writeValueAsString(objectIHttpResult);} catch (JsonProcessingException e) {e.printStackTrace();throw new RuntimeException("Conversion string result exception");}}return objectIHttpResult;}}}

上述代码兼容旧代码,并且提供了白名单和对String返回值的特殊处理。

企业项目之spring项目《接口响应体格式统一封装》最终实现

1.全局异常处理类

package com.global.format.config;import com.global.format.response.DefaultHttpResult;import com.global.format.response.constant.PlatformCode;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;/** *  * @author * */@ControllerAdvicepublic class GlobalExceptionHandler {private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);@SuppressWarnings("rawtypes")@ExceptionHandler(Exception.class)public @ResponseBody    Object exceptionHandler(Exception exception) {DefaultHttpResult httpResult = new DefaultHttpResult();if (exception instanceof MethodArgumentNotValidException) {BindingResult bindingResult = ((MethodArgumentNotValidException) exception).getBindingResult();StringBuilder strBuilder = new StringBuilder();for (FieldError fieldError : bindingResult.getFieldErrors()) {strBuilder.append(fieldError.getDefaultMessage()).append("/");}String msg = strBuilder.toString();logger.error("捕获到参数校验异常:{}, exception:", msg, exception);httpResult.setCode(PlatformCode.PARAM_ERROR.getCode());httpResult.setMessage(msg);} else {logger.error("捕获到系统错误:{}, exception:", exception.getMessage(), exception);httpResult.setCode(PlatformCode.SYSTEM_ERROR.getCode());httpResult.setMessage(exception.getMessage());}return httpResult;}}

2.全局响应值组装类

package com.global.format.config;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.global.format.response.DefaultHttpResult;import com.global.format.response.DefaultPageResp;import com.global.format.response.IHttpResult;import com.global.format.response.IPageResp;import com.global.format.response.constant.PlatformCode;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.util.Arrays;import java.util.List;/** * * @Author yy **/@SuppressWarnings("rawtypes")@ControllerAdvice(basePackages = "com.global.format.controller")public class GlobalResponseBodyAdvice implements ResponseBodyAdvice {private static final Logger logger = LoggerFactory.getLogger(GlobalResponseBodyAdvice.class);//可以使用白名单排查有些不组装结果的接口,如监控,监控检查等接口private static final List WHITE_LIST_PATH = Arrays.asList( "/test/common" );@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SuppressWarnings({ "unchecked" })@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {String requestPath = request.getURI().getPath();if (WHITE_LIST_PATH.contains(requestPath)) {logger.info("GDX-DEBUG request path:{} in response body advice white list.", requestPath);return body;}// 返回值空值处理if (body == null) {DefaultHttpResult httpResult = new DefaultHttpResult();httpResult.setCode(PlatformCode.SUCCESS.getCode());httpResult.setMessage(PlatformCode.SUCCESS.getMessage());httpResult.setData(null);return httpResult;}// 兼容Rpc返回值规范if (body instanceof IPageResp) {IPageResp pageResp = (IPageResp) body;DefaultHttpResult httpResult = new DefaultHttpResult();httpResult.setCode(PlatformCode.SUCCESS.getCode());httpResult.setMessage(PlatformCode.SUCCESS.getMessage());httpResult.setData(DefaultPageResp.buildPageResp(pageResp.getPageNum(), pageResp.getPageSize(),pageResp.getTotalCount(), pageResp.getPageRecords()));return httpResult;} // 兼容之前APi规范老代码else if (body instanceof IHttpResult) {return body;} else {// 将新返回值统一封装到api规范中IHttpResult objectIHttpResult = DefaultHttpResult.successWithData(body);if (body instanceof String) {// String需要特殊处理,SpringMVC会根据返回接口参数为对象时将当前返回值当成String处理,会出现强转异常try {//jsonManager对象可以处理为单例return new ObjectMapper().writeValueAsString(objectIHttpResult);} catch (JsonProcessingException e) {e.printStackTrace();throw new RuntimeException("Conversion string result exception");}}return objectIHttpResult;}}}

3.其余的是普通的常量类和响应值类,包含分页类等,如有需要直接下载源码。

结束语

接口响应体统一格式转化,这个功能就介绍完了,其实是非常简单的,代码复制粘贴太累,直接上github地址:https://github.com/Wismyluckstar/Spring-tool

大家多多关注支持下,后续还需更新更多有用的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值