废话不多说,直接正文。
首先,我们需要定义规则(返回的格式)。如http的返回状态码 404,500,200等。我们都知道404是找不到资源,200是正常的。由此可以知道,每一个状态码都有一个对应的说明。当请求是正常的,我们还需要有存储返回值的地方。
所以返回的格式如下:状态码(code),状态码说明(msg),相应数据(data),这个是比较基础的返回格式。
/*** 返回体* @author**/public class Result implements Serializable {/*** */private static final long serialVersionUID = 1L;//状态码private Integer code;//响应消息private String msg;//响应数据private Object data;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public Result(ResultCode resultCode,Object data) {this.code=resultCode.getCode();this.msg=resultCode.getMsg();this.data=data;}public Result(Integer code,String msg,Object data) {this.code=code;this.msg=msg;this.data=data;}}
当然也有更复杂的。如下格式:包含分页信息、token身份、模糊查询等。(这个的实现和上面的格式没有什么区别。就是需要建立的实体类要多一些。)
{"message": {"header": {"auth": {"callerid": "1552267897663","license": "9425846d54cf4ff0b7d81dd6b922fa75","token": "095A1DA8106527EE81350845611AEEB3","ipaddr": "","macaddr": ""},"servicecode": {"systemcode": "","busscode": "","version": "1","apiurl": ""},"respon": {"code": "1","text": "Token验证正常","detail": null}},"body": {"bussresponse": {"code": "0","text": "系统异常:系统错误:传入查询编号不适配","detail": "0038e266-5c10-49ef-a614-e4e16257208d"},"queryresponse": {"pageno": 1,"pagesize": "","sortfiled": null,"sorttype": null,"condition": []}"datacompress": "","dataencrypt": "","dataset": [{"name": "","row": []}]},"extend": {}}}
回归正题,建完实体类后,建枚举定义各个状态码以及说明。
public enum ResultCode {//成功SUCCESS(200,"成功"),PARAM_IS_INVALID(1001,"参数无效"),PARAM_IS_BLANK(1002,"参数为空"),PARAM_IS_ERROR(1004,"参数错误"),PARAM_IS_COMPLETE(1003,"参数缺失"),USER_LONGIN_ERROR(2001,"账号或密码错误"),USER_LONGIN_EXIST(2002,"用户不存在"),USER_LONGIN_EXISTD(,"用户已存在"),KHXX_EXISTD(,"客户信息已存在!"),USER_LONGIN_EXAMINE(,"用户待审核"),UPDATE_EXAMINE(,"修改申请已提交,等待管理员审核"),USER_LONGIN_STOP(,"用户已停用"),ERROR(500,"系统异常"),CUSTOMER_EXISTD(400,"客户信息已存在"),PHONE_ERROR(402,"验证码或者手机号错误"),PHONE_EXIST(405,"手机号为空"),PHONE_RETRY(405,"请2分钟后重试"),PHONE_FPRMATEXIST(406,"手机号错误"),SINE_ERROR(401,"用户信息已过期,请重新登录"),LONG_OVERTIME(406,"登录超时,请重新登录"),KHXX_HTJH_EXISTD(1005,"该类型的计划已存在"),USER_INSUFFICIENT_AUTHORITY(403,"权限不足"), FILE_NOTBLANK(,"上传文件不能为空"),FILE_MAXSIZE(,"上传文件不能超过2M"),FILE_ERRORSUFFIX(,"上传文件错误,只能上传文档或表格以及PDF格式");private Integer code;private String msg;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}ResultCode(Integer code,String msg){this.code=code;this.msg=msg;}}
这样返回体的格式就定义完了。只需要在代码中使用就行,如下:
public Result queryKhht(JSONObject csjson) {if (!csjson.containsKey("khid")) {return new Result(ResultCode.PARAM_IS_ERROR,"客户编号不能为空");}//获取客户合同List<Map<String, Object>> khktList = khxxDao.queryKhht(csjson.getInteger("khid"));if (khktList.size() >0) {for (int i = 0; i < khktList.size(); i++) {Map<String, Object> khhtMap=khktList.get(i);//获取客户附件List<Map<String, Object>> khhtfjList = khxxDao.queryKhhtfj(Integer.valueOf(khhtMap.get("aid").toString()));if (khhtfjList.size()>0) {khhtMap.put("fjdata", khhtfjList);}//获取合同回款计划List<Map<String, Object>> hkjhList = khxxDao.queryKhhthkjh(Integer.valueOf(khhtMap.get("aid").toString()));if (hkjhList.size()>0) {khhtMap.put("children", hkjhList);}}}return new Result(ResultCode.SUCCESS,khktList);}
查询结果展示:
当参数为空时,也可以给到前端明确的提示,如下(ResultCode.PARAM_IS_ERROR对应的枚举是1004,参数错误)
返回结果展示:
可能有的小伙伴也发现了,这样写每一个方法都需要
new Result(枚举,"");看着也不美观。我想只需要return 返回数据体就行 。
下面我们就添加拦截器来实现这个功能。拦截器代码如下。
package com.jmz.ControllerAdvice;import javax.servlet.http.HttpServletRequest;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 com.jmz.entity.Result;/*** Controller返回数据后 拦截封装响应体* @author **/@ControllerAdvicepublic class ResponseResultHandler implements ResponseBodyAdvice<Object> {//标记名称private static final String PRESENT_NA_ANN="PRESENT_NA_ANN";/*判断是否需要包装返回,不需要直接返回即可*/public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {Long startTime = System.currentTimeMillis();ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();//根据标记名获取对象 在ResponseResultInterceptor类写入ResponseResult rue =(ResponseResult) request.getAttribute(PRESENT_NA_ANN);return rue== null ? true : false;}/*判断body是否属于返回体的类,属于直接返回body否则封装后在返回*/public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {Long startTime = System.currentTimeMillis();if (body instanceof Result) {return body;}return new Result(ResultCode.SUCCESS,body);}}
测试使用,Controller直接返回list集合
@RequestMapping("/khxx/getKhdtxx")public List<Map<String, Object>> getKhdtxx(@Validated @RequestBody CrmKhdt khdt) {return khxxService.getKhdtxx(khdt);}
postman返回
到此,统一返回体就完成了。但是有一个问题。就是如果我们有部分方法的返回值不需要包装呢?我们现在已经拦截了所有的返回体进行了封装。
所有我这边加了一个自定义注解@ResponseResult来实现这一功能。当我们不需要包装时,只需要在这个方法加上@ResponseResult注解就可以了。
自定义注解@ResponseResult
package com.jmz.ControllerAdvice;import java.lang.annotation.*;/*** * @author * 自定义放行Release注解,添加该注解放回值不包装**/@Target(ElementType.METHOD)@Documented@Retention(RetentionPolicy.RUNTIME)public @interface ResponseResult {}
验证方法是否包含该注解,如果存在@ResponseResult就不需要封装返回体。
package com.jmz.ControllerAdvice;import javax.servlet.http.HttpServletRequest;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 com.jmz.entity.Result;/*** Controller返回数据后 拦截封装响应体* @author **/@ControllerAdvicepublic class ResponseResultHandler implements ResponseBodyAdvice<Object> {//标记名称private static final String PRESENT_NA_ANN="PRESENT_NA_ANN";/*判断是否需要包装返回,不需要直接返回即可*/public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {Long startTime = System.currentTimeMillis();ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();//根据标记名获取对象 在ResponseResultInterceptor类写入ResponseResult rue =(ResponseResult) request.getAttribute(PRESENT_NA_ANN);return rue== null ? true : false;}/*判断body是否属于返回体的类,属于直接返回body否则封装后在返回*/public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {Long startTime = System.currentTimeMillis();if (body instanceof Result) {return body;}return new Result(ResultCode.SUCCESS,body);}}
测试使用
@RequestMapping("/sing/AESDncode")@Release@ResponseResultpublic String AESDncode(HttpServletRequest request) {return AESUtils.AESDncode(request.getParameter("encodeRules"), request.getParameter("content"));}@RequestMapping("/sing/AESEncode")@Release@ResponseResultpublic String AESEncode(HttpServletRequest request) {return AESUtils.AESEncode(request.getParameter("encodeRules"), request.getParameter("content"));}