springboot统一接口返回数据的实现

网友投稿 911 2023-04-11

springboot统一接口返回数据的实现

springboot统一接口返回数据的实现

一,没有异常的情况,正常返回数据

希望接口统一返回的数据格式如下:

{

"status": 0,

"msg": "成功",

"data": null

}

和接口数据对应的bean

/**

* 统一返回结果的实体

* @param

*/

public class Result implements Serializable {

private static final long serialVersionUID = 1L;

/**

* 错误码

*/

private int status;

/**

* 提示消息

*/

private String msg;

/**

* 返回的数据体

*/

private T data;

public int getStatus() {

return status;

}

public void setStatus(int status) {

this.status = status;

}

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;

}

}

操作Result实体的工具类

/**

* 生成result的工具类,避免重复代码

*/

public class ResultUtils {

/**

* 成功时生成result的方法,有返回数据

*/

public static Result success(T t){

Result result = new Result<>();

result.setStatus(ResultEnum.SUCCESS.getCode());

result.setMsg(ResultEnum.SUCCESS.getMsg());

result.setData(t);

return result;

}

/**

* 成功时生成result的方法,无返回数据

*/

public static Result success(){

return success(null);

}

/**

* 失败时生成result的方法

*/

public static Result error(int status, String msg){

Result result = new Result<>();

result.setStatus(status);

result.setMsg(msg);

return result;

}

}

封装错误码和错误消息的枚举类

/**

* 所有返回结果的枚举

*/

public enum ResultEnum {

UNKNOWN_ERROR(-1, "未知错误"),

SUCCESS(0, "成功"),

BASIC_INFO_ID_IS_EMPTY(600, "基本信息中BasicInfoId为空"),

BASIC_INFO_ADD_TO_DATABASE_FAILURE(601, "向数据库添加基本信息失败"),

DETAILS_DATA_BASIC_INFO_ID_IS_EMPTY(602, "测试数据中BasicInfoId为空"),

DETAILS_DATA_ADD_TO_DATABASE_FAILURE(603, "向数据库添加测试数据失败");

ResultEnum(int code, String msg) {

this.code = code;

this.msg = msg;

}

private int code;

private String msg;

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;

}

@Override

public String toString() {

return "ResultEnum{" +

"code=" + code +

", msg='" + msg + '\'' +

'}';

}

}

统一封装返回结果的切面

之所以需要这个切面,是为了避免每个Controller方法中都要调用ResultUtils.success()。有了这个切面,Controller可以和原来一样正常返回对象,字符串,void,在切面里面将结果封装成Result实体,而不需要每个Controller方法都返回Result实体。

/**

* 统一处理返回结果的切面,避免每个contrMqXaZPoller方法里面都要调用ResultUtils.success()这句话

* 统一在这个切面里面调用

*/

@ControllerAdvice

public class MyResponseAdvice implements ResponseBodyAdvice {

@Autowired

private ObjectMapper objectMapper;

/**

* Whether this component supports the given controller method return type

* and the selected {@code HttpMessageConverter} type.

*

* @param returnType the reMqXaZPturn type

* @param converterType the selected cMqXaZPonverter type

* @return {@code true} if {@link #beforeBodyWrite} should be invoked;

* {@code false} otherwise

*/

@Override

public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) {

return true;

}

/**

* Invoked after an {@code HttpMessageConverter} is selected and just before

* its write method is invoked.

*

* @param body the body to be written

* @param returnType the return type of the controller method

* @param selectedContentType the content type selected through content negotiation

* @param selectedConverterType the converter type selected to write to the response

* @param request the current request

* @param response the current response

* @return the body that was passed in or a modified (possibly new) instance

*/

@Override

public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

if(body instanceof Result){ //发生异常之后,异常处理器里面返回的已经是Result了

return body;

}else if(body instanceof String){ //String属于特殊情况,需要单独处理,否则会报错

try {

return objectMapper.writeValueAsString(ResultUtils.success(body));

} catch (jsonProcessingException e) {

e.printStackTrace();

return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage());

}

}

return ResultUtils.success(body);

}

}

二,有异常的情况下

service层为了自动回滚事务,会抛出一些自定义的RuntimeException。默认情况下,只有RuntimeException才会回滚事务。如果Controller里面直接处理service层抛出的异常,则Controller里面到处都是try catch块,代码会很难看。将异常集中在一个地方处理会好很多。

springboot中是通过@ControllerAdvice和@ExceptionHandler来完成统一异常处理的。这2个注解只能处理Controller和-中抛出的异常,其他地方抛出的异常(比如Filter中抛出的异常),无法捕获。其他地方抛出的异常会转到/error的Controller方法来处理,默认是BasicErrorController来处理,为了能处理其他地方抛出的异常,我们会自定义ErrorController。

统一的异常处理类,处理Controller和-抛出的异常

/**

* 统一的异常处理类

*/

@ControllerAdvice

public class MyExceptionHandler {

/**

* 转发到/error,表示由BasicErrorController处理,

* BasicErrorController是由springboot自动装配到容器中的

*/

/*@ExceptionHandler(BasicInfoException.class)

public String handleException(Exception ex, HttpServletRequest request){

request.setAttribute("javax.servlet.error.status_code", 401);

request.setAttribute("exMsg", ex.getMessage());

return "forward:/error";

}*/

/**

* 处理基本信息相关的异常

*/

@ExceptionHandler(BasicInfoException.class)

@ResponseBody

public Result handleBasicInfoException(BasicInfoException ex){

return ResultUtils.error(ex.getCode(), ex.getMessage());

}

/**

* 处理测试数据相关的异常

*/

@ExceptionHandler(DetailsDataException.class)

@ResponseBody

public Result handleDetailsDataException(DetailsDataException ex){

return ResultUtils.error(ex.getCode(), ex.getMessage());

}

/**

* 处理未知异常

*/

@ExceptionHandler(Exception.class)

@ResponseBody

public Result handleUnKnowException(Exception ex){

return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), ex.getMessage());

}

}

自定义的异常类示例

public class BasicInfoException extends RuntimeException {

private int code;

public BasicInfoException(int code, String msg){

super(msg);

this.code = code;

}

public int getCode() {

return code;

}

}

处理其他地方抛出的异常(不是Controller和-抛出的异常),自定义ErrorController

/**

* 自定义ErrorController,处理其他地方抛出的异常(不是Controller和-抛出的异常)

*/

@Controller

public class MyBasicErrorController extends AbstractErrorController {

private Logger logger = LoggerFactory.getLogger(this.getClass());

/**

* 可以通过@Value获取到

*/

@Value("${server.error.path}")

private String myPath;

private final ErrorProperties errorProperties;

private ErrorAttributes mErrorAttributes;

public MyBasicErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) {

super(errorAttributes);

this.errorProperties = serverProperties.getError();

this.mErrorAttributes = errorAttributes;

}

//@RequestMapping(value = "/error")

@RequestMapping("${server.error.path}") //从properties文件中获取

@ResponseBody

public Result error(HttpServletRequest request) throws Throwable {

logger.debug("myPath = " + myPath);

//发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理

WebRequest webRequest = new ServletWebRequest(request);

Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause();

throw throwable;

/*UserException ex;

if(throwable instanceof UserException){

ex = (UserException) throwable;

throw ex;

}else{

throw throwable;

}*/

/*HttpStatus status = getStatus(request);

if (status == HttpStatus.NO_CONTENT) {

return ResultUtils.error(status.value(), status.name());

}

Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));

return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/

}

/**

* Determine if the stacktrace attribute should be included.

* @param request the source request

* @param produces the media type produced (or {@code MediaType.ALL})

* @return if the stacktrace attribute should be included

*/

private boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {

ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();

if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {

return true;

}

if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {

return getTraceParameter(request);

}

return false;

}

/**

* Provide access to the error properties.

* @return the error properties

*/

private ErrorProperties getErrorProperties() {

return this.errorProperties;

}

/**

* Returns the path of the error page.

*

* @return the error path

*/

@Override

public String getErrorPath() {

return this.errorProperties.getPath();

}

}

自定义ErrorController中错误处理的方法中,也可以直接将异常抛出,这样异常就会交给统一异常处理器进行处理。

//@RequestMapping(value = "/error")

@RequestMapping("${server.error.path}") //从properties文件中获取

@ResponseBody

public Result error(HttpServletRequest request) throws Throwable {

logger.debug("myPath = " + myPath);

//发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理

WebRequest webRequest = new ServletWebRequest(request);

Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause();

UserException ex;

if(throwable instanceof UserException){

ex = (UserException) throwable;

throw ex;

}else{

throw throwable;

}

/*HttpStatus status = getStatus(request);

if (status == HttpStatus.NO_CONTENT) {

return ResultUtils.error(status.value(), status.name());

}

Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));

return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot Security前后端分离登录验证的实现
下一篇:电脑端qq如何打开小程序(电脑端如何打开小程序功能)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~