# LY-controller **Repository Path**: laishuailin/ly-controller ## Basic Information - **Project Name**: LY-controller - **Description**: 对java中controller 常用设置规范 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-01-17 - **Last Updated**: 2023-08-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # LY-controller #### 介绍 对java中controller 常用设置规范 主要涉及到接口返回值规范,参数校验,异常处理等规范 #### 接口返回规范 ``` @Data public class RespVol { /* * 状态消息 * * */ private String message; /* * 状态码 * * */ private Integer code; /* * 消息体 * * */ private Object data; /* * 手动设置返回值 * * */ public RespVol(String message, Integer code, Object data) { this.message = message; this.code = code; this.data = data; } /* * 默认返回成功 * * */ public RespVol(Object data){ this.message= SystemCode.SUCCESS.getMessage(); this.code= SystemCode.SUCCESS.getCode(); this.data=data; } //返回指定状态码,数据对象 public RespVol(StatusCode statusCode,Object data){ this.message=statusCode.getMessage(); this.code=statusCode.getCode(); this.data=data; } //返回指定状态码 public RespVol(StatusCode statusCode){ this.message=statusCode.getMessage(); this.code=statusCode.getCode(); this.data=null; } } ``` #### 接口返回统一进行包装 ``` @RestControllerAdvice public class ControllerResponseAdvice implements ResponseBodyAdvice { //对特定的规则的接口继续包装 @Override public boolean supports(MethodParameter returnType, Class> converterType) { //对接口返回值为RespVol不进行包装 if(returnType.getParameterType().isAssignableFrom(RespVol.class)){ return false; } if(returnType.hasMethodAnnotation(NotControllerResponseAdvice.class)){ return false; } return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // String类型不能直接包装 if (returnType.getGenericParameterType().equals(String.class)) { ObjectMapper objectMapper = new ObjectMapper(); try { // 将数据包装在ResultVo里后转换为json串进行返回 return objectMapper.writeValueAsString(new RespVol(body)); } catch (JsonProcessingException e) { throw new ApiException(SystemCode.RESPONSE_PACK_ERROR, e.getMessage()); } } // 否则直接包装成ResultVo返回 return new RespVol(body); } } ``` 对不需要统一返回的定义NotControllerResponseAdvice.class的注解 ``` @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface NotControllerResponseAdvice { } //使用注解的方式进行 @GetMapping("/notString") @NotControllerResponseAdvice public String testNotString(){ return "hello"; } ``` 对进行了返回值包装类型的接口可直接放回对应的实体类不需要进行封装 ``` //使用包装类返回 结构数据 @GetMapping("/pack") public StudentDto testPack(){ StudentDto studentDto=new StudentDto(); studentDto.setName("张三"); studentDto.setScore(80F); studentDto.setArgs(12); studentDto.setGrade(3); return studentDto; } ``` #### 参数校验规范 在请求接口是经常使用POST和GET,在进行请求时参数需要进行验证。对RequestBody的验证如下 RequestBodyEntity ``` @Data public class StudentRequest { @NotNull(message ="学生姓名不能为空") private String name; @Min(value = 0, message = "学生年龄不允许为负数") private Integer age; } ``` Controller中的配置 ``` /* * 参数校验 * * */ @PostMapping("/sec") public RespVol testSec(@Validated @RequestBody StudentRequest studentRequest){ StudentDto studentDto=new StudentDto(); studentDto.setName("张三"); studentDto.setScore(80F); studentDto.setArgs(12); studentDto.setGrade(3); return new RespVol(studentDto); } ``` #### 异常事件处理 定义系统报错ERROR_CODE message ``` public enum SystemCode implements StatusCode{ SUCCESS(1000,"请求成功"), FAILED(1001,"请求失败"), REQUEST_BODY_PARAM_VALIDATE_ERROR(1002, "参数校验失败"), RESPONSE_PACK_ERROR(1003, "response返回包装失败"), REQUEST_PARAM_MISS_ERROR(1004,"参数漏传"), REQUEST_PARAM_TYPE_ERROR(1005,"请求参数类型不匹配"); } ``` MethodArgumentNotValidException.class 获取校验参数异常 MissingServletRequestParameterException.class 漏传RequestParam中要求的参数异常 MethodArgumentTypeMismatchException.class RequestParam中要求的参数类型异常 ``` /** * 参数校验异常 * * * */ @ExceptionHandler(MethodArgumentNotValidException.class) public RespVol paramValidThrow(MethodArgumentNotValidException e) { List objectErrors= e.getBindingResult().getAllErrors(); StringBuffer message=new StringBuffer(); objectErrors.stream().forEach(value ->{ message.append(value.getDefaultMessage()); message.append(";"); }); return new RespVol(SystemCode.REQUEST_BODY_PARAM_VALIDATE_ERROR,message); } /** * 参数漏传 * * * */ @ExceptionHandler(MissingServletRequestParameterException.class) public RespVol missParamValueThrowHandler(MissingServletRequestParameterException e){ return new RespVol(SystemCode.REQUEST_PARAM_MISS_ERROR,e.getMessage()) ; } /** * 请求参数类型不匹配 * * * */ @ExceptionHandler(MethodArgumentTypeMismatchException.class) public RespVol requestParamValueThrowHandler(MethodArgumentTypeMismatchException e){ return new RespVol(SystemCode.REQUEST_PARAM_TYPE_ERROR,e.getMessage()); } ``` 捕获业务异常定义异常捕获的Execption ``` public class ApiException extends RuntimeException{ private Integer code; private String msg; } ``` 使用 @RestControllerAdvice 和 @ExceptionHandler捕获抛出的业务异常 ``` /** * 业务异常 * * * */ @ExceptionHandler(ApiException.class) public RespVol appExceptionThrowHandler(ApiException e){ return new RespVol(e.getMsg(),e.getCode(),e.getMessage()); } ``` 抛出异常的实例 ``` /** * 业务异常 * * * */ @ExceptionHandler(ApiException.class) public RespVol appExceptionThrowHandler(ApiException e){ return new RespVol(e.getMsg(),e.getCode(),e.getMessage()); } ```