大多数项目都已经前后端分离,前端的小伙伴们当然希望接口的定义更规范,更方便。所以接口返回的数据包含至少包含以下三个属性:
- code:请求接口的返回码,比如200表示成功,500表示异常。
- msg:请求接口的描述,本次请求后台返回的信息。
- data:请求接口成功,返回的结果。
封装前后结果对比:
String:
map:
pageInfo
怎么实现?
实现思路:1.定义返回实体Resp,2.所有接口统一返回Resp(真实情况会用3替代)
进阶思路:使用上面的方法索然实现了统一处理返回值,但是会有一些问题,比如老接口怎么办?解决办法:3.自定义拦截器处理 或者交由springmvc的MappingJackson2HttpMessageConverter处理
再次进阶:正常数据都处理好了,遇到异常怎么办?4.自定义异常处理
大致如下:
具体实现
一.定义返回实体Resp
import lombok.Data;
/**
*
*/
@Data
public class Resp<T> {
/**响应码 200:成功 非200:异常*/
private String code="200";
/** 前端提示信息 */
private String msg="成功";
/** 数据体:对象数组或者字符串,根据接口区分 */
private T data;
}
二.接口统一定义返回类型
这种方法不推荐,后边通过spring代替这种手动处理
/**
* 统一格式的返回结果
* @param req
* @return
*/
@RequestMapping("test1")
public Resp test1(@Valid @RequestBody ProjectGoodExampleReq req) {
Resp resp = new Resp();
resp.setData("123");
return resp;
}
三(一).自定义拦截器处理
package com.ridge.project.core.convert;
import com.ridge.project.core.resp.Resp;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
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;
/**
* 使用 @ControllerAdvice & ResponseBodyAdvice
* 拦截Controller方法默认返回参数,统一处理返回值/响应体
*/
@ControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {
// 判断是否要执行 beforeBodyWrite 方法,true为执行,false不执行,有注解标记的时候处理返回值
@Override
public boolean supports(MethodParameter arg0, Class<? extends HttpMessageConverter<?>> arg1) {
return true;
}
// 对返回值做包装处理
@Override
public Object beforeBodyWrite(Object body, MethodParameter arg1, MediaType arg2,
Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest arg4, ServerHttpResponse arg5) {
Resp resp = new Resp();
if (body instanceof Resp) {
return body;
} else if (body instanceof String) {
return body;
}
resp.setData(body);
return resp;
}
}
三(二).继承springmvc的MappingJackson2HttpMessageConverter,重写writeInternal方法
import com.fasterxml.jackson.core.JsonGenerator;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.ridge.project.core.exception.CODE;
import com.ridge.project.core.page.BizDataPage;
import com.ridge.project.core.resp.Resp;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
/**
* 消息转换器
**/
@Configuration
public class JsonMessageConverter extends MappingJackson2HttpMessageConverter {
public JsonMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
//设置日期格式
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
objectMapper.setDateFormat(smt);
//**数字转成字符串*//*
objectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
//**不返回值为null的属性*//*
//objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
//设置中文编码格式
List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION_JSON_UTF8);
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(list);
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
Resp resEntity = new Resp();
if (object instanceof Resp) {
super.writeInternal(object, type, outputMessage);
return;
} else if (object instanceof PageInfo || object instanceof Page) {
resEntity.setCode(CODE.success.getCode());
resEntity.setData(new BizDataPage<>(object));
} else {
resEntity.setCode(CODE.success.getCode());
resEntity.setData(object);
}
super.writeInternal(resEntity, type, outputMessage);
}
}
注意:这里推荐用三(二)这种方法,因为即使用来三(一),最后的处理结果还是有三(二)来执行的
四.统一异常处理
继承RuntimeException,在需要的地方直接抛异常就可以了
import lombok.Getter;
public class BizException extends RuntimeException{
@Getter
CODE code;
public BizException(CODE code){
super(code.getMsg());
this.code = code;
}
/**
* 处理自定义错误信息
* @param code
* @param message 自定义错误提示信息
*/
public BizException(CODE code,String message){
super(message);
code.setMsg(message);
this.code = code;
}
public BizException(CODE code,Exception e){
super(e);
this.code = code;
}
}
抛异常示例
/**
* 统一格式的返回结果-异常处理
* @param req
* @return
*/
@RequestMapping("test1")
public String test1(@Valid @RequestBody ProjectGoodExampleReq req) {
throw new BizException(CODE.biz_error, "测试");
}