全局响应支持显式响应和隐式响应,异常处理支持自定义异常和非自定义异常
废话不多说,直接上代码,优雅性,扩展性相当可以,嘿嘿
代码结构:

Http状态码枚举类,响应的httpcode,最好是统一管理
/*** 描述:请求时的自定义查询状态码,主要参考Http状态码,但并不完全对应 <br>* 时间:2020/7/30 5:15 <br>* 作者:老王**/public enum HttpCode {/** 10系统异常 */SYS_EXCEPTION(10),/** 200请求成功 */OK(200),/** 207频繁操作 */MULTI_STATUS(207),/** 400请求参数出错 */BAD_REQUEST(400),/** 401没有登录 */UNAUTHORIZED(401),/** 4012没有登录-没token匹配-登录超时*/UNAUTHORIZED2(4012),/** 4013 用户被限制登录*/UNAUTHORIZED3(4013),/** 4014没有登录-没token匹配-异地登录*/UNAUTHORIZED4(4014),/** 402登录失败 */LOGIN_FAIL(402),/** 403没有权限 */FORBIDDEN(403),/** 404找不到页面 */NOT_FOUND(404),/** 405请求方法不能被用于请求相应的资源 */METHOD_NOT_ALLOWED(405),/** 406内容特性不满足 */NOT_ACCEPTABLE(406),/** 408请求超时 */REQUEST_TIMEOUT(408),/** 409发生冲突 */CONFLICT(409),/** 410已被删除 */GONE(410),/** 411没有定义长度 */LENGTH_REQUIRED(411),/** 412条件不满足 */PRECONDITION_FAILED(412),/** 413数据太大 */ENTITY_TOO_LARGE(413),/** 415不是服务器中所支持的格式 */UNSUPPORTED_MEDIA_TYPE(415),/** 421连接数过多 */TOO_MANY_CONNECTIONS(421),/** 423已被锁定 */LOCKED(423),/** 451法律不允许 */UNAVAILABLE_LEGAL(451),/** 500服务器出错 */SERVER_ERROR(500),/** 503服务器升级中,暂时不可用 */SERVICE_UNAVAILABLE(503),/** 501获取资源所需要的策略并没有被满足 */NOT_EXTENDED(510);public final Integer code;private HttpCode(Integer code) {this.code = code;}/*** Return the integer value of this status code.*/public Integer code() {return this.code;}@Overridepublic String toString() {return this.code.toString();}}
json转化辅助类JsonUtil处理json和对象/列表/map之间的转换
package com.utils;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.JsonInclude;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.core.JsonFactory;import com.fasterxml.jackson.core.JsonGenerator;import com.fasterxml.jackson.core.JsonParser;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.core.type.TypeReference;import com.fasterxml.jackson.databind.*;import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;import java.io.IOException;import java.io.StringWriter;import java.util.List;import java.util.Map;/*** @author 老王* @date 2018/12/24 11:21 上午*/public class JsonUtil {public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();/*** 初始化ObjectMapper* @return*/private static ObjectMapper createObjectMapper() {ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);// 允许序列化空的POJO类(否则会抛出异常)objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);// 对象的所有字段全部列入。NON_NULL:不返回 null 值字段objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);// 取消java.util.Date, Calendar默认转换timestamps形式objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS , false);objectMapper.registerModule(new JavaTimeModule());// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicobjectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);return objectMapper;}public static String object2Json(Object o) {StringWriter sw = new StringWriter();JsonGenerator gen = null;try {gen = new JsonFactory().createGenerator(sw);OBJECT_MAPPER.writeValue(gen, o);} catch (IOException e) {throw new RuntimeException("不能序列化对象为Json", e);} finally {if (null != gen) {try {gen.close();} catch (IOException e) {throw new RuntimeException("不能序列化对象为Json", e);}}}return sw.toString();}/*** 对象转map* @param o 转行对象* @return*/public static Map<String, Object> object2Map(Object o) {return OBJECT_MAPPER.convertValue(o,Map.class);}/*** map转java对象* @param map 参数map* @param clazz T字节对象* @param <T> 返回对象类型* @return*/public static <T> T map2Object(Map map, Class<T> clazz) {return OBJECT_MAPPER.convertValue(map,clazz);}/*** 将 json 字段串转换为 对象.** @param json 字符串* @param clazz 需要转换为的类* @return*/public static <T> T json2Object(String json, Class<T> clazz) {try {return OBJECT_MAPPER.readValue(json, clazz);} catch (IOException e) {throw new RuntimeException("将 Json 转换为对象时异常,数据是:" + json, e);}}/*** 将 json 字段串转换为 List.* @param json* @param clazz* @param <T>* @return* @throws IOException*/public static <T> List<T> json2List(String json,Class<T> clazz) throws IOException {JavaType type = OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz);return OBJECT_MAPPER.readValue(json, type);}/*** 将 json 字段串转换为 数据.* @param json* @param clazz* @param <T>* @return* @throws IOException*/public static <T> T[] json2Array(String json,Class<T[]> clazz) throws IOException {return OBJECT_MAPPER.readValue(json, clazz);}public static <T> T node2Object(JsonNode jsonNode, Class<T> clazz) {try {T t = OBJECT_MAPPER.treeToValue(jsonNode, clazz);return t;} catch (JsonProcessingException e) {throw new RuntimeException("将 Json 转换为对象时异常,数据是:" + jsonNode.toString(), e);}}public static JsonNode object2Node(Object o) {try {if(o == null) {return OBJECT_MAPPER.createObjectNode();} else {return OBJECT_MAPPER.convertValue(o, JsonNode.class);}} catch (Exception e) {throw new RuntimeException("不能序列化对象为Json", e);}}/*** JsonNode转换为Java泛型对象,可以是各种类型。** @param <T>* @param json String* @param tr TypeReference,例如: new TypeReference< List<FamousUser> >(){}* @return List对象列表*/public static <T> T json2GenericObject(String json, TypeReference<T> tr) {if (json == null || "".equals(json)) {throw new RuntimeException("将 Json 转换为对象时异常,数据是:" + json);} else {try {return (T) OBJECT_MAPPER.readValue(json, tr);} catch (Exception e) {throw new RuntimeException("将 Json 转换为对象时异常,数据是:" + json, e);}}}}
自定义的异常类
package com.http.exception;import java.io.PrintWriter;import java.io.StringWriter;/*** 描述: 基础异常 <br>* 时间: 2020-02-07 16:18 <br>* 作者:老王*/public class BaseException extends RuntimeException {private int code;private String message;public BaseException() {}public BaseException(int code, String message) {super(message);this.code = code;this.message = message;}public BaseException(String msg, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(msg, cause, enableSuppression, writableStackTrace);}public BaseException(String message, Throwable cause) {super(message, cause);}public BaseException(String message) {super(message);}public BaseException(Throwable cause) {super(cause);}public static String getStackTrace(Throwable throwable) {StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw, true);throwable.printStackTrace(pw);return sw.getBuffer().toString();}public int getCode() {return code;}public void setCode(int code) {this.code = code;}@Overridepublic String getMessage() {return message;}public void setMessage(String message) {this.message = message;}@Overridepublic String toString() {return "BaseException{" +"code=" + code +", msg='" + message + '\'' +'}';}}
package com.http.exception;import com.http.constant.HttpCode;/*** 描述: 业务异常 <br>* 时间: 2020-02-07 16:18 <br>* 作者:老王*/public class BusinessException extends BaseException {private int code = HttpCode.SERVER_ERROR.code;private String message;public BusinessException() {}public BusinessException(int code, String message) {super(code, message);this.code = code;this.message = message;}public BusinessException(Throwable ex) {super(ex);}public BusinessException(String message) {this(HttpCode.SERVER_ERROR.code, message);}public BusinessException(String msg, Throwable ex) {super(msg, ex);}@Overridepublic int getCode() {return code;}@Overridepublic void setCode(int code) {this.code = code;}@Overridepublic String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
package com.http.exception;/*** 描述: BusinessException业务异常描述 <br>* 时间: 2020-02-24 15:16 <br>* 作者:老王*/public enum BEMsg {user_login_name_exist("登录名已存在"),login_user_error("用户名不存在或密码错误"),vote_repeat("您已点赞过"),no_auth_error("您无权进行当前操作"),no_Sign_error("请登录后再进行当前操作"),;public String message;BEMsg(String message) {this.message = message;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}
下面就是统一响应和处理的代码
全局结果响应实体Result
package com.http.response;import java.io.Serializable;/*** 全局结果响应实体 <br>* 作者:老王 <br>* 时间:2019-01-24 10:33*/public class Result<T> implements Serializable {/**响应的code码*/private int code;/**响应的success布尔值*/private boolean success;/**响应的message*/private String msg;/**响应的业务数据*/private T data;public Result(int code, String msg, T data){this.code=code;this.msg=msg;this.data=data;}public Result(int code, boolean success, String msg, T data) {this.code = code;this.success = success;this.msg = msg;this.data = data;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;}public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}}
响应的状态枚举
package com.http.response;/*** 描述: 响应状态枚举 <br>* 时间: 2020-01-25 19:33 <br>* 作者:老王*/public enum ResponseStateEnum {success("success"),error("error");public String value;ResponseStateEnum(String state) {this.value = state;}ResponseStateEnum() {}public String getValue() {return value;}public void setValue(String value) {this.value = value;}}
全局结果响应建造者,方便构建result对象
package com.http.response;import com.http.constant.HttpCode;/*** 全局结果响应建造者 <br>* 作者:老王 <br>* 时间:2019-01-24 10:33*/public class ResultBuilder {//自定义message 失败信息public static <T> Result<T> err (String message) {return new Result<T>(HttpCode.SERVER_ERROR.code,false, message,null);}//自定义message 失败信息public static <T> Result<T> err (String message, T data) {return new Result<T>(HttpCode.SERVER_ERROR.code, false, message, data);}//自定义message 失败信息public static <T> Result<T> ok (String message) {return new Result<T>(HttpCode.OK.code, true, message,null);}//自定义message 成功public static <T> Result<T> ok (String message, T data) {return new Result<T>(HttpCode.OK.code,true, message, data);}//自定义code,msg 返回数据public static <T> Result<T> render(int code, boolean success, String msg) {return new Result<T>(code, success, msg,null);}//自定义code,msg,data 返回数据public static <T> Result<T> render(int code, boolean success, String msg, T data) {return new Result<T>(code, success, msg, data);}}
全局结果异常响应实体,本质和Result一样,只是单独处理异常的数据响应结构
package com.http.response;/*** 全局结果异常响应实体<br>* 作者:老王 <br>* 时间:2019-01-24 10:33*/public class ResultException {/**响应码*/private int code;private boolean success = false;/**响应消息提醒*/private String msg;/**请求的异常路径*/private String url;public ResultException(int code, String msg, String url) {this.code = code;this.msg = msg;this.url = url;}public ResultException(int code, boolean success, String msg, String url) {this.code = code;this.success = success;this.msg = msg;this.url = url;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}}
全局结果响应处理,全局异常处理
package com.http.response;import cn.hutool.core.exceptions.ExceptionUtil;import com.http.constant.HttpCode;import com.http.exception.BusinessException;import com.utils.JsonUtil;import lombok.SneakyThrows;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.validation.BindException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.servlet.http.HttpServletRequest;/*** 全局结果响应处理,全局异常处理 <br>* 作者:老王 <br>* 时间:2019-01-24 10:33*/@Slf4j@RestControllerAdvicepublic class ResultAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {return true;}/*** 响应前重写处理* @param o 方法响应返回实体* @param methodParameter 方法参数* @param mediaType* @param aClass* @param serverHttpRequest request* @param serverHttpResponse response* @return Object - 重写方法响应返回实体后的对象*/@SneakyThrows@Overridepublic Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {return builderResult(o);}/*** 重写方法返回对象响应结构* @param o* @return*/private Object builderResult(Object o) {//如果响应体Result,直接返回,不做处理if (o instanceof Result) {return o;}//返回数据data是String的时候需要做出处理,不然会报错if (o instanceof String) {Result<String> result = ResultBuilder.ok(ResponseStateEnum.success.value, o.toString());return JsonUtil.object2Json(result);}//返回的数据若是异常返回信息,那么不做处理直接返回if (o instanceof ResultException) {return o;}return ResultBuilder.ok(ResponseStateEnum.success.value, o);}/*** 先捕获异常 然后再把数据返回到ResponseBody中,* 然后在Body中要返回数据的时候调用上面的拦截方法beforeBodyWrite()*/@ExceptionHandler(value = Exception.class)public Object handleException(Exception e, HttpServletRequest request) {//此处返回json数据//捕捉到的异常如果是自定义异常类,那么就返回自定义异常类中的错误码和错误信息String stackExceptionMsg = ExceptionUtil.stacktraceToString(e);//异常输出到日志log.error(stackExceptionMsg);//自定义基础异常if (e instanceof BusinessException) {return new ResultException(((BusinessException) e).getCode(), false, ((BusinessException) e).getMessage(), request.getRequestURL().toString());//非法参数异常} else if (e instanceof IllegalArgumentException) {return new ResultException(HttpCode.BAD_REQUEST.code, false, "参数异常,请稍候再试", request.getRequestURL().toString());//绑定异常} else if (e instanceof BindException) {return new ResultException(HttpCode.BAD_REQUEST.code, false, ((BindException) e).getBindingResult().getFieldError().getDefaultMessage(), request.getRequestURL().toString());//方法参数异常验证异常} else if (e instanceof MethodArgumentNotValidException) {return new ResultException(HttpCode.BAD_REQUEST.code, false, ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage(), request.getRequestURL().toString());}//这里是除了自定义异常的其他异常信息else {return new ResultException(HttpCode.SERVER_ERROR.code, false, "系统异常请联系管理员", request.getRequestURL().toString());}}}
这同加了@RestControllerAdvice后,就会在@RestController的控制器上返回时,对响应结果进行处理重写咯,
流程如下:
@RestController的控制器响应后 -->到加了@RestControllerAdvice的ResultAdvice ,在 beforeBodyWrite 方法中重构响应体Result--> Result由 ResultBuilder 来构建
在@ExceptionHandler(value = Exception.class)的方法
handleException上进行统一异常处理
hutool的ExceptionUtil控制日志输出,不然全局异常捕获,异常日志就无法输出,不利于问题排查,其他的都是自定义异常和其他异常的统一处理,处理成全局响应结果ResultException,比Result就多了一个url,之所以这么玩,是因为不同的处理我喜欢用不同的对象处理,相互不干扰,但是返回后的json格式还是一样的,前端可以用一个对象就可以接收
下面测试demo演示
User对象
package com.model;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;/*** 描述: 用户model </br>* 时间: 2020-12-14 9:59 </br>* 作者:老王*/@Data@AllArgsConstructor@NoArgsConstructorpublic class User {private Integer id;private String userName;private Integer age;}
package com.controller;import com.http.exception.BaseException;import com.http.response.Result;import com.http.response.ResultBuilder;import com.model.User;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** 描述: 统一结果响应测试控制器 <br>* 时间: 2022-06-07 8:33 <br>* 作者:老王*/@RestController@RequestMapping("/respTest")public class RespTestController {/*** 隐式响应,即方法响应体就是业务实体,优点直观明了*/@GetMapping("/getUser")public User getUser(){User user = new User(1, "IT学习道场", 18);return user;}/*** 显式响应,即方法响应体就是result,* 优点可以自定义一些定制化的响应数据组装* 但是需要自己构建Result对象*/@GetMapping("/getResult")public Result<User> getResult(){User user = new User(1, "IT学习道场", 18);//可以根据不同的业务处理渲染不同的Result,增加了灵活性Result<User> result = ResultBuilder.render(200, true, "只是我自己构建的Result", user);return result;}/*** 测试统一异常处理(自定义异常)*/@GetMapping("/testBaseException")public User testBaseException(){throw new BaseException(508, "触发自定义业务异常了");}/*** 测试统一异常处理(非自定义异常)*/@GetMapping("/testException")public User testException(){throw new RuntimeException("业务异常了");}}
public User getUser(){} 响应Result结构
{"code": 200,"success": true,"msg": "success","data": {"id": 1,"userName": "IT学习道场","age": 18}}
public Result<User> getResult(){} 响应Result结构
{"code": 200,"success": true,"msg": "只是我自己构建的Result","data": {"id": 1,"userName": "IT学习道场","age": 18}}
public User testBaseException(){} 响应Result结构
{"code": 500,"success": false,"msg": "系统异常请联系管理员","url": "http://localhost:8080/respTest/testBaseException"}
public User testException()响应Result结构
{"code": 500,"success": false,"msg": "系统异常请联系管理员","url": "http://localhost:8080/respTest/testException"}
你学废了吗?




