java 8 mediasize错误,SpringBoot - 实用工具类库common-util使用详解12(数据通用返回格式、全局异常处理)...

十一、数据通用返回格式

1,什么是统一数据返回格式?

(1)前后端分离是当今服务形式的主流,为了让前端有更好的逻辑展示与页面交互处理,统一的数据返回格式必不可少。通常每次一次 RESTful请求的返回数据都应该包含类似如下几个信息:

success:标识请求成功与否,如:true(成功)、false(失败)

code:错误码,如果异常的话则为明确错误码,从而更好的对应业务异常。如果请求成功该值可为空或者“0000”

message:错误消息,与错误码相对应,更具体的描述异常信息。

data:返回结果,通常是 Bean对象对应的 JSON数据,通常为了应对不同返回值类型,将其声明为泛型类型

timestamp:执行时间戳

(2)而common-util 这个工具库也为我们提供了一个现成的通用返回数据封装类 CommonResult,所属的包为com.power.common.model,我们直接使用即可。

2,执行成功响应

(1)无返回结果:

@RestController

public class HelloController {

@RequestMapping("/test")

public CommonResult test() {

return CommonResult.ok();

}

}

86a11f6616b811679acb2ed5655654d9.png

(2)返回一个对象:

@RestController

public class HelloController {

@RequestMapping("/test")

public CommonResult test() {

Book book = new Book(1, "东野圭吾", "沉默的巡游", 32f);

return CommonResult.ok().setResult(book);

}

}

2df702f403dc2d8ff5ad02e883e84720.png

(3)返回一个集合:

@RestController

public class HelloController {

@RequestMapping("/test")

public CommonResult test() {

List books= new ArrayList<>();

books.add(new Book(1, "东野圭吾", "沉默的巡游", 32f));

books.add(new Book(2, "鲁迅", "彷徨", 2.99f));

return CommonResult.ok().setResult(books);

}

}

8ee6ff819b21afc58f017749aa371acc.png

3,执行失败响应

(1)不指定错误信息,以及错误响应码:

@RestController

public class HelloController {

@RequestMapping("/test")

public CommonResult test() {

return CommonResult.fail();

}

}

9458763fe39d77ea547449920ad856a0.png

(2)指定错误信息,以及错误响应码:

@RestController

public class HelloController {

@RequestMapping("/test")

public CommonResult test() {

return CommonResult.fail("1002", "参数格式错误");

}

}

52f2557f1ca5d96614081eea8fe59287.png

(3)当然实际开发中为了维护方便,我们首先会定义了一个 ErrorCode枚举:

提示:IMessage接口也是 common-util 工具类库中提供的。

public enum ErrorCodeEnum implements IMessage {

SUCCESS("0000", "succeed"),

PARAM_EMPTY("1001", "必选参数为空"),

PARAM_ERROR("1002", "参数格式错误"),

UNKNOWN_ERROR("9999", "系统繁忙,请稍后再试....");

private String code;

private String message;

ErrorCodeEnum(String errCode, String errMsg) {

this.code = errCode;

this.message = errMsg;

}

@Override

public String getCode() {

return this.code;

}

@Override

public String getMessage() {

return this.message;

}

}

(4)然后使用这个错误信息枚举即可:

@RestController

public class HelloController {

@RequestMapping("/test")

public CommonResult test() {

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR);

}

}

附:实现统一数据返回格式的自动封装

上面的样例我们都是手动将返回值封装成 CommonResult对象并返回,但如果每个 API都需要做这些重复的工作会显的不够优雅。下面演示如何结合@RestControllerAdvice 注解实现返回数据的自动封装。

1,实现返回数据的自动转换

(1)首先我们创建如下自定义的 Handler,其作用是对于com.hangge.controller 包下的所有 controller:

如果返回值是 CommonResult对象的话则不做处理

如果不是的话会自动封装成 CommonResult对象

// 全局的返回数据自动转换

@RestControllerAdvice("com.hangge.controller")

class RestResponseHandler implements ResponseBodyAdvice {

@Override

public boolean supports(MethodParameter methodParameter,

Class extends HttpMessageConverter>> aClass) {

return true;

}

@Override

public Object beforeBodyWrite(Object body, MethodParameter methodParameter,

MediaType mediaType,

Class extends HttpMessageConverter>> aClass,

ServerHttpRequest serverHttpRequest,

ServerHttpResponse serverHttpResponse) {

// 如果返回值已经是 CommonResult,则不做处理直接返回

if (body instanceof CommonResult){

return body;

}

// 否则的话封装成 CommonResult 再返回

CommonResult commonResult = CommonResult.ok().setResult(body);

// 如果controller层中返回的类型是String,我们还需要特殊处理下(将CommonResult对象转回String)

if (body instanceof String) {

// 这里我使用 FastJSON 进行转换

return JSON.toJSONString(commonResult);

}

return commonResult;

}

}

(2)测试一下,虽然我们 controller里面返回的是一个 List,但从请求结果可以发现,最终得到的是封装后的 CommonResult对象:

@RestController

public class HelloController {

@RequestMapping("/test")

public List test() {

List books= new ArrayList<>();

books.add(new Book(1, "东野圭吾", "沉默的巡游", 32f));

books.add(new Book(2, "鲁迅", "彷徨", 2.99f));

return books;

}

}

8ee6ff819b21afc58f017749aa371acc.png

(3)如果 controller没有返回值,也是会得到 CommonResult对象的:

@RestController

public class HelloController {

@RequestMapping("/test")

public void test() {

}

}

86a11f6616b811679acb2ed5655654d9.png

2,全局异常处理

(1)上面的配置只是实现了对正常数据的自动封装,当程序发生异常时,我们希望也能返回统一的数据格式到前台。这个只添加如下全局异常处理 Handler即可。无论是 controller层里抛出的异常,还是请求没有进入 controller层(比如发生 401、403等请求错误),都是可以返回通过格式。

// 全局的Rest异常处理

@RestControllerAdvice

public class RestExceptionHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionHandler.class);

// 处理参数验证异常

@ExceptionHandler(value = {MethodArgumentNotValidException.class})

@ResponseStatus(HttpStatus.BAD_REQUEST)

public CommonResult illegalParamsExceptionHandler(MethodArgumentNotValidException ex) {

BindingResult bindingResult = ex.getBindingResult();

FieldError fieldError = bindingResult.getFieldError();

LOGGER.error("request params invalid: {}", fieldError.getDefaultMessage());

return processBindingError(fieldError);

}

// 处理参数转换失败异常

@ExceptionHandler(value = {MethodArgumentTypeMismatchException.class})

@ResponseStatus(HttpStatus.BAD_REQUEST)

public CommonResult methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex)

{

String error = String.format("The parameter '%s' should be of type '%s'", ex.getName(),

ex.getRequiredType().getSimpleName());

return CommonResult.fail("400", error);

}

// 处理资源找不到异常(404)

@ExceptionHandler(value = {NoHandlerFoundException.class})

@ResponseStatus(HttpStatus.NOT_FOUND)

public CommonResult noHandlerFoundException(Exception ex) {

return CommonResult.fail("404", "Resource Not Found");

}

// 处理不支持当前媒体类型异常(415)

@ExceptionHandler(value = {HttpMediaTypeNotSupportedException.class})

@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)

public CommonResult handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex) {

StringBuilder builder = new StringBuilder();

builder.append(ex.getContentType());

builder.append(" media type is not supported. Supported media types are ");

ex.getSupportedMediaTypes().forEach(t -> builder.append(t).append(","));

return CommonResult.fail("415", builder.toString());

}

// 处理方法不被允许异常(405)

@ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})

@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)

public CommonResult methodNotSupportedException(HttpRequestMethodNotSupportedException ex) {

LOGGER.error("Error code 405: {}", ex.getMessage());

return CommonResult.fail("405", ex.getMessage());

}

// 处理其他异常(错误码统一为500)

@ExceptionHandler(value = {Exception.class})

@ResponseStatus(HttpStatus.OK)

public CommonResult unknownException(Exception ex) {

LOGGER.error("Error code 500:{}", ex);

return new CommonResult("500", ex.getMessage());

}

// 处理参数验证异常(转换成对应的CommonResult)

private CommonResult processBindingError(FieldError fieldError) {

String code = fieldError.getCode();

LOGGER.debug("validator error code: {}", code);

switch (code) {

case "NotEmpty":

return CommonResult.fail(ErrorCodeEnum.PARAM_EMPTY.getCode(),

fieldError.getDefaultMessage());

case "NotBlank":

return CommonResult.fail(ErrorCodeEnum.PARAM_EMPTY.getCode(),

fieldError.getDefaultMessage());

case "NotNull":

return CommonResult.fail(ErrorCodeEnum.PARAM_EMPTY.getCode(),

fieldError.getDefaultMessage());

case "Pattern":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Min":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Max":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Length":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Range":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Email":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "DecimalMin":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "DecimalMax":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Size":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Digits":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Past":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

case "Future":

return CommonResult.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),

fieldError.getDefaultMessage());

default:

return CommonResult.fail(ErrorCodeEnum.UNKNOWN_ERROR);

}

}

}

(2)假设我们在 Controller中随便抛出一个异常,可以看到请求响应显示的是通用返回格式:

@RestController

public class HelloController {

@RequestMapping("/test")

public void test() {

throw new MaxUploadSizeExceededException(1000);

}

}

11e4a81f7569f49cd49cde1455638c66.png

(3)但对于 404错误(比如我们访问一个不存在的地址),会发现并没有返回统一格式。这是因为 Spring Boot 默认不会抛出 404异常(NoHandlerFoundException),所以在 ControllerAdvice中捕获不到该异常,导致 404总是跳过 ContollerAdvice,直接显示 ErrorController的错误页。

springboot的 WebMvcAutoConfiguration会默认配置如下资源映射:

/映射到 /static(或 /public、/resources、/META-INF/resources)

/webjars/ 映射到 classpath:/META-INF/resources/webjars/

/**/favicon.ico映射 favicon.ico文件.

c65a0384d5cb057967e3470ab6340676.png

(4)要解决这个问题,我们在 application.properties 中添加如下两行配置即可,这样 NoHandlerFoundException 异常就能被@ControllerAdvice捕获了:

注意:

配置修改后,我们如果需要访问静态文件前面就需要加上 /static。比如:http://localhost:8080/static/java.png

当然如果我们是一个纯后台应用,没有静态文件的话,可以直接将第二个配置改成 spring.resources.add-mappings=false,不要为我们工程中的资源文件建立映射。

spring.mvc.throw-exception-if-no-handler-found=true

spring.mvc.static-path-pattern=/static/**

765717bfacd8ff272ca1ffe508d8d073.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值