前言
当初咱们做我的项目基本上中大型项目都是抉择前后端拆散,前后端拆散曾经成了一个趋势了,所以总这样·咱们就要和前端约定对立的api 接口返回json 格局,
这样咱们须要封装一个对立通用全局 模版api返回格局,下次再写我的项目时候间接拿来用就能够了
约定JSON格局
个别咱们和前端约定json格局是这样的
{ "code": 200, "message": "胜利", "data": { }}
- code: 返回状态码
- message: 返回信息的形容
data: 返回值
封装java bean
定义状态枚举
package cn.soboys.core.ret;import lombok.Data;import lombok.Getter;/** * @author kenx * @version 1.0 * @date 2021/6/17 15:35 * 响应码枚举,对应HTTP状态码 */@Getterpublic enum ResultCode { SUCCESS(200, "胜利"),//胜利 //FAIL(400, "失败"),//失败 BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, "认证失败"),//未认证 NOT_FOUND(404, "接口不存在"),//接口不存在 INTERNAL_SERVER_ERROR(500, "零碎忙碌"),//服务器外部谬误 METHOD_NOT_ALLOWED(405,"办法不被容许"), /*参数谬误:1001-1999*/ PARAMS_IS_INVALID(1001, "参数有效"), PARAMS_IS_BLANK(1002, "参数为空"); /*用户谬误2001-2999*/ private Integer code; private String message; ResultCode(int code, String message) { this.code = code; this.message = message; }}
定义返回状态码,和信息一一对应,咱们能够约定xxx~xxx 为什么错误码,避免前期错误码反复,应用凌乱不分明,
定义返回体后果体
package cn.soboys.core.ret;import lombok.Data;import java.io.Serializable;/** * @author kenx * @version 1.0 * @date 2021/6/17 15:47 * 对立API响应后果格局封装 */@Datapublic class Result<T> implements Serializable { private static final long serialVersionUID = 6308315887056661996L; private Integer code; private String message; private T data; public Result setResult(ResultCode resultCode) { this.code = resultCode.getCode(); this.message = resultCode.getMessage(); return this; } public Result setResult(ResultCode resultCode,T data) { this.code = resultCode.getCode(); this.message = resultCode.getMessage(); this.setData(data); return this; }}
code,和message都从定义的状态枚举中获取
这里有两个须要留神中央我的数据类型T data
返回的是泛型
类型而不是object
类型而且我的后果累实现了Serializable
接口
我看到网上有很多返回object,最初返回泛型因为泛型效率要高于object,object须要强制类型转换,还有最初实现了Serializable
接口因为通过流bytes传输方式web传输,速率更块
定义返回后果办法
个别业务返回要么是 success
胜利,要么就是failure
失败,所以咱们须要独自定义两个返回实体对象办法,
package cn.soboys.core.ret;/** * @author kenx * @version 1.0 * @date 2021/6/17 16:30 * 响应后果返回封装 */public class ResultResponse { private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; // 只返回状态 public static Result success() { return new Result() .setResult(ResultCode.SUCCESS); } // 胜利返回数据 public static Result success(Object data) { return new Result() .setResult(ResultCode.SUCCESS, data); } // 失败 public static Result failure(ResultCode resultCode) { return new Result() .setResult(resultCode); } // 失败 public static Result failure(ResultCode resultCode, Object data) { return new Result() .setResult(resultCode, data); }}
留神这里我定义的是动态工具办法,因为应用构造方法进行创建对象调用太麻烦了, 咱们应用静态方法来就间接类调用很不便
这样咱们就能够在controller中很不便返回对立api格局了
package cn.soboys.mallapi.controller;import cn.soboys.core.ret.Result;import cn.soboys.core.ret.ResultResponse;import cn.soboys.mallapi.bean.User;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author kenx * @version 1.0 * @date 2021/7/2 20:28 */ @RestController //默认全副返回json@RequestMapping("/user")public class UserController { @GetMapping("/list") public Result getUserInfo(){ User u=new User(); u.setUserId("21"); u.setUsername("kenx"); u.setPassword("224r2"); return ResultResponse.success(u); }}
返回后果合乎咱们预期json格局
然而这个代码还能够优化,不够欠缺,比方,每次controller中所有的办法的返回必须都是要Result类型,咱们想返回其余类型格局怎么半,还有就是不够语义化,其余开发人员看你办法基本就不晓得具体返回什么信息
如果改成这个样子就完满了如
@GetMapping("/list") public User getUserInfo() { User u = new User(); u.setUserId("21"); u.setUsername("kenx"); u.setPassword("224r2"); return u; }
其余开发人员一看就晓得具体是返回什么数据。但这个格局要怎么去对立进去?
其实咱们能够这么去优化,通过SpringBoot提供的ResponseBodyAdvice
进行对立响应解决
- 自定义注解@ResponseResult来拦挡有此controller注解类的代表须要对立返回json格局,没有就安照原来返回
package cn.soboys.core.ret;import java.lang.annotation.*;/** * @author kenx * @version 1.0 * @date 2021/6/17 16:43 * 对立包装接口返回的值 Result */@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ResponseResult {}
- 定义申请拦截器通过反射获取到有此注解的HandlerMethod设置包装拦挡标记
package cn.soboys.core.ret;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;/** * @author kenx * @version 1.0 * @date 2021/6/17 17:10 * 申请拦挡 */public class ResponseResultInterceptor implements HandlerInterceptor { //标记名称 public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //申请办法 if (handler instanceof HandlerMethod) { final HandlerMethod handlerMethod = (HandlerMethod) handler; final Class<?> clazz = handlerMethod.getBeanType(); final Method method = handlerMethod.getMethod(); //判断是否在对象上加了注解 if (clazz.isAnnotationPresent(ResponseResult.class)) { //设置此申请返回体须要包装,往下传递,在ResponseBodyAdvice接口进行判断 request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class)); //办法体上是否有注解 } else if (method.isAnnotationPresent(ResponseResult.class)) { //设置此申请返回体须要包装,往下传递,在ResponseBodyAdvice接口进行判断 request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class)); } } return true; }}
- 实现
ResponseBodyAdvice<Object>
接口自定义json返回解析器依据包装拦挡标记判断是否须要自定义返回类型返回类型
package cn.soboys.core.ret;import cn.soboys.core.utils.HttpContextUtil;import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j;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.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.servlet.http.HttpServletRequest;/** * @author kenx * @version 1.0 * @date 2021/6/17 16:47 * 全局对立响应返回体解决 */@Slf4j@ControllerAdvicepublic class ResponseResultHandler implements ResponseBodyAdvice<Object> { public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN"; /** * @param methodParameter * @param aClass * @return 此处如果返回false , 则不执行以后Advice的业务 * 是否申请蕴含了包装注解 标记,没有间接返回不须要重写返回体, */ @Override public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { HttpServletRequest request = HttpContextUtil.getRequest(); //判断申请是否有包装标记 ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN); return responseResultAnn == null ? false : true; } /** * @param body * @param methodParameter * @param mediaType * @param aClass * @param serverHttpRequest * @param serverHttpResponse * @return 解决响应的具体业务办法 */ @Override public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { if (body instanceof Result) { return body; } else if (body instanceof String) { return JSON.toJSONString(ResultResponse.success(body)); } else { return ResultResponse.success(body); } }}
留神这里string类型返回要独自json序列化返回一下,不然会报转换异样
这样咱们就能够在controler中返回任意类型,了不必每次都必须返回 Result
如
package cn.soboys.mallapi.controller;import cn.soboys.core.ret.ResponseResult;import cn.soboys.core.ret.Result;import cn.soboys.core.ret.ResultResponse;import cn.soboys.mallapi.bean.User;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author kenx * @version 1.0 * @date 2021/7/2 20:28 */@RestController //默认全副返回json@RequestMapping("/user")@ResponseResultpublic class UserController { @GetMapping("/list") public User getUserInfo() { User u = new User(); u.setUserId("21"); u.setUsername("kenx"); u.setPassword("224r2"); return u; } @GetMapping("/test") public String test() { return "ok"; } @GetMapping("/test2") public Result test1(){ return ResultResponse.success(); }}
这里还有一个问题?失常状况返回胜利的话是对立json 格局,然而返回失败,或者异样了,怎么对立返回谬误json 格局,sprinboot有本人的谬误格局?
请参考我上一篇,SpringBoot优雅的全局异样解决
扫码关注公众号猿人生理解更多好文