赞
踩
具体设计思路可以参考 响应篇1–理论
异常返回可以参考 响应篇2–异常返回
// 正常响应
{
"data": ["data1", "data2", "data3"...],// 响应数据
"code": 200,// 业务状态码
"msg": "登陆成功", // 业务详细信息
"links": {// API相关的其他接口信息
"link1": xxx,
"link2": xxx,
},
"paging": {// 分页字段,当有分页才显示
"total": 3,// 总页数
"size": 5,// 页面容量
"page": 2,// 当前页面
"prev": "http://localhost:8080/statistics?page=1&size=5",// 前一页url
"next": "http://localhost:8080/statistics?page=3&size=5"// 后一页url
}
}
/**
* @Author geek_lazy
*/
// 篇幅原因,使用 @Data 省略 getter 和 setter 方法
@Data
public class RestfulResult<T> {
private T data;// 响应数据
private int code;// 业务状态码
private String msg;// 业务详细信息
private Page paging;// 分页信息
private String links;// 相关链接
private Object errors;// 多个错误信息(如验证)
public RestfulResult() {
// 无参构造的时候给一个成功的默认值
this.code = ResultCode.SUCCESS.code();
this.msg = ResultCode.SUCCESS.msg();
this.data = data;
}
// 这里我只写一个构造方法,根据情况可以自己定义
public RestfulResult(ResultCode code, T data) {
this.code = code.code();
this.msg = code.msg();
this.data = data;
}
}
/**
* @Author geek_lazy
*/
@Data
public class Page {
private long total;// 总页数
private int page;// 当前页
private int size;// 页面容量
private String prev;// 前一页url
private String next;// 后一页url
public Page(long total, int page, int size) {
this.total = total;
this.page = page;
this.size = size;
}
}
/**
* @Author geek_lazy
*/
@Data
public enum ResultCode {
UNKNOWN_ERROR(0, "未知错误"),// 默认错误
SUCCESS(1, "成功"),
PARAM_IS_INVALID(10001, "参数无效"),
USER_NOT_LOGGED_IN(20001, "用户未登录"),
// ...
;
private int code;// 业务状态码
private String msg;// 业务详细信息
private HttpStatus httpStatus;// 业务对应 HTTP 状态码
ResultCode(int code, String msg, HttpStatus httpStatus) {
this.code = code;
this.msg = msg;
this.httpStatus = httpStatus;
}
}
到目前为止, 其实已经初步完成 RESTful 返回格式的几个关键类了, 如果要求不高, 我们就可以使用一下试试看~
一般情况下, controller大致如下:
controller
/**
* @Author geek_lazy
*/
@RestController
@RequestMapping("/statistics")
public class StatisticController {
// service层我就不写了,理解为上
@Autowired
private IStatisticService iss;
/**
* 获取一个Statistic对象
*/
@GetMapping("{statisticId}")
public Statistic getStatisticById(@PathVariable("statisticId") String statisticId) {
return iss.getStatisticById(statisticId);
}
/**
* 获取Statistic对象列表,这里使用PageHelper插件
*/
@GetMapping
public PageInfo<Statistic> getStatisticList(@RequestParam("page") Integer pageNum, @RequestParam("size") Integer pageSize) {
return iss.getStatisticList(pageNum, pageSize);
}
}
怎么将返回变成我们想要的格式 —— RestfulResult 呢? 尝试了几种方法:
可以, 但这样就要改变所有接口的返回值, 很不优雅
@GetMapping
public RestfulResult<Statistic> getStatisticList(@RequestParam("page") Integer pageNum, @RequestParam("size") Integer pageSize) {
PageInfo<Statistic> pageInfo = iss.getStatisticList(pageNum, pageSize);
RestfulResultt<Statistic> resultResult = new RestfulResult();
restfulResult.setData(pageInfo.getList());
restfulResult.setPage(new Page(pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPageSize()))
return resultResult;
}
方便, 但不能支持HATEOAS和业务状态码设置
/**
* @Author geek_lazy
*/
@Configuration
public class RestfulHttpMessageConverter {
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
return new MappingJackson2HttpMessageConverter() {
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// 参数中的 object 即为controller返回的对象
RestfulResult restfulResult = new RestfulResult();
// 判断 object 是不是 PageHelper 插件的 PageInfo 类型, 即是否分页接口
if (!(object instanceof PageInfo)) {// object 不是 PageInfo 对象
// 将数据设置到 restfulResult 的 data属性里
restfulResult.setData(object);
// 转换为json并写入输出流
super.writeInternal(restfulResult, type, outputMessage);
return;
}
// object 是 PageInfo 对象
PageInfo pageInfo = ((PageInfo) object);
// 判断数据是不是分页查询了(pageNum==0的时候返回的是全表数据)
if (pageInfo.getPageNum() == 0) {// 全表数据
// 将数据设置到 restfulResult 的 data属性里
restfulResult.setData(pageInfo.getList());
}else {// 分页数据
// 将数据设置到 restfulResult 的 data属性里
restfulResult.setData(pageInfo.getList());
// 将分页信息设置到 restfulResult 的 page属性里
restfulResult.setPaging(new Page(pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPageSize()));
}
super.writeInternal(restfulResult, type, outputMessage);
}
};
}
}
Interceptor
/**
* @Author geek_lazy
*/
@Component
public class ResultFormatInterceptor implements HandlerInterceptor {
public static final String RESULT_FORMAT = "RESULT_FORMAT";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 判断 handler 是否是方法级
if (handler instanceof HandlerMethod) {// 是
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final Class<?> clazz = handlerMethod.getBeanType();// 拦截方法所属类
final Method method = handlerMethod.getMethod();// 拦截方法
// 判断类或方法是否被ResultFormat注解
if (clazz.isAnnotationPresent(ResultFormat.class) || method.isAnnotationPresent(ResultFormat.class)) {// 是
// request中设置字段说明接口返回需要被格式化,这是因为ResponseBodyAdvice不能获取到注解信息
request.setAttribute(RESULT_FORMAT, clazz.getAnnotation(ResultFormat.class));
}
}
// 继续执行
return true;
}
}
@ControllerAdvice
/**
* @Author geek_lazy
*/
@ControllerAdvice
public class ResultFormatHandler implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
ResultFormat resultFormatAnn = (ResultFormat) RequestContextHolderUtils.getRequest().getAttribute(ResultFormatInterceptor.RESULT_FORMAT);
// 判断是requestAttr中是否存在RESULT_FORMAT
return resultFormatAnn != null;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest req, ServerHttpResponse resp) {
ResultFormat responseResultAnn = (ResultFormat) RequestContextHolderUtils.getRequest().getAttribute(ResultFormatInterceptor.RESULT_FORMAT);
Class<? extends Result> resultClazz = responseResultAnn.value();
// 判断是不是RestfulResult的父类????
if (resultClazz.isAssignableFrom(RestfulResult.class)) {// 是
RestfulResult restfulResult = new RestfulResult();
if (resultClazz.isInstance(o)) {// 已经被格式化过了(如统一异常处理)
return o;
}
if (!(o instanceof PageInfo)) {// 不是PageInfo类,没有分页结果
restfulResult.setData(o);
return restfulResult;
}
PageInfo pageInfo = (PageInfo) o;
// 判断是否分页
if (pageInfo.getPageNum() == 0) {// pageNum = 0 即返回所有数据,没有分页
restfulResult.setData(pageInfo.getList());
} else {// 分页了
restfulResult.setData(pageInfo.getList());
restfulResult.setPaging(new Page(pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPageSize()));
}
return restfulResult;
}
return null;
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。