本篇文章为你整理了如何写出优雅的Controller层代码(controller代码怎么写)的详细内容,包含有controller层怎么写 controller代码怎么写 controller 层 controller层的作用 如何写出优雅的Controller层代码,希望能帮助你了解 如何写出优雅的Controller层代码。
请求方式(一般就是 get、set,当然还有 put、delete)
请求数据(request,有 head 跟 body)
响应数据(response)
Controller 主要的工作有以下几项:
接收请求并解析参数
调用 Service 执行具体的业务代码(可能包含参数校验)
捕获业务逻辑异常做出反馈
业务逻辑执行成功做出响应
解决以下 3 个问题:
返回响应数据该如何统一的进行处理
接收到请求,处理业务逻辑时抛出了异常又该如何处理
当接收到请求时,如何优雅的校验参数
统一返回结构
封装Result
package com.wanqi.result;
public interface IResult {
Integer getCode();
String getMessage();
定义状态码
package com.wanqi.result;
public enum ResultEnum implements IResult {
SUCCESS(2001, "接口调用成功"),
VALIDATE_FAILED(2002, "参数校验失败"),
COMMON_FAILED(2003, "接口调用失败"),
FORBIDDEN(2004, "没有权限访问资源"),
DATA_CONVERSION(2005, "数据转换异常");
private Integer code;
private String message;
ResultEnum(int code, String message) {
this.code=code;
this.message=message;
@Override
public Integer getCode() {
return code;
@Override
public String getMessage() {
return message;
Result包装类
package com.wanqi.result;
public class Result T {
private Integer code;
private String message;
private T data;
public Integer getCode() {
return code;
public void setCode(Integer code) {
this.code = code;
public String getMessage() {
return message;
public void setMessage(String message) {
this.message = message;
public T getData() {
return data;
public void setData(T data) {
this.data = data;
public Result() {
public Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
public static T Result T success(T data) {
return new Result (ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);
public static T Result T success(String message, T data) {
return new Result (ResultEnum.SUCCESS.getCode(), message, data);
public static Result ? failed() {
return new Result (ResultEnum.COMMON_FAILED.getCode(), ResultEnum.COMMON_FAILED.getMessage(), null);
public static Result ? failed(String message) {
return new Result (ResultEnum.COMMON_FAILED.getCode(), message, null);
public static Result ? failed(Integer code,String message) {
return new Result (code, message, null);
public static Result ? failed(IResult errorResult) {
return new Result (errorResult.getCode(), errorResult.getMessage(), null);
public static T Result T instance(Integer code, String message, T data) {
Result T result = new Result ();
result.setCode(code);
result.setMessage(message);
result.setData(data);
return result;
统一包装处理
package com.wanqi.advice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wanqi.exception.APIException;
import com.wanqi.result.Result;
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.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
// 如果引入了swagger或knife4j的文档生成组件,这里需要仅扫描自己项目的包,否则文档无法正常生成
@RestControllerAdvice(basePackages = "com.wanqi.controller")
public class ResponseAdvice implements ResponseBodyAdvice Object {
@Override
public boolean supports(MethodParameter returnType, Class ? extends HttpMessageConverter ? converterType) {
// response是Result类型不进行包装
return !returnType.getParameterType().isAssignableFrom(Result.class);
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class ? extends HttpMessageConverter ? selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(returnType.getGenericParameterType().equals(String.class)){
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.writeValueAsString(Result.success(body));
} catch (JsonProcessingException e) {
throw new APIException(e.getMessage());
return Result.success(body);
Controller层
package com.wanqi.controller;
import com.wanqi.pojo.TestDTO;
import com.wanqi.pojo.UserDTO;
import org.hibernate.validator.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@RestController
public class TestController {
@PostMapping("/test")
public TestDTO test(@RequestBody TestDTO testDTO) {
return testDTO;
@PostMapping("/test1")
public String test1(@RequestBody TestDTO testDTO) {
return testDTO.getName();
不统一处理返回
自定义注解
package com.wanqi.advice;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {
在增强过滤方法上过滤包含这个注解的方法
@RestControllerAdvice(basePackages = "com.wanqi.controller")
public class ResponseAdvice implements ResponseBodyAdvice Object {
@Override
public boolean supports(MethodParameter returnType, Class ? extends HttpMessageConverter ? converterType) {
// response是Result类型,或者注释了NotControllerResponseAdvice都不进行包装
return !(returnType.getParameterType().isAssignableFrom(Result.class)
returnType.hasMethodAnnotation(NotControllerResponseAdvice.class));
.....
controller
在不需要统一处理的方法上使用@NotControllerResponseAdvice注解即可
package com.wanqi.controller;
import com.wanqi.advice.NotControllerResponseAdvice;
import com.wanqi.pojo.TestDTO;
import com.wanqi.pojo.UserDTO;
import com.wanqi.result.Result;
import org.hibernate.validator.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Validated
@RestController
public class TestController {
@GetMapping("/health")
@NotControllerResponseAdvice
public String health() {
return "success";
@GetMapping("/health1")
public Result health1() {
return Result.success("dddd");
自定义异常与统一拦截异常
自定义异常
package com.wanqi.exception;
import com.wanqi.result.ResultEnum;
//自定义异常
public class APIException extends RuntimeException {
private int code;
private String msg;
public int getCode() {
return code;
public String getMsg() {
return msg;
public APIException(String message) {
super(message);
this.code = ResultEnum.DATA_CONVERSION.getCode();
this.msg = ResultEnum.DATA_CONVERSION.getMessage();
统一拦截异常
package com.wanqi.advice;
import com.wanqi.exception.APIException;
import com.wanqi.result.IResult;
import com.wanqi.result.Result;
import com.wanqi.result.ResultEnum;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
//统一拦截异常
@RestControllerAdvice(basePackages = "com.wanqi")
public class ControllerExceptionAdvice {
* 捕获 {@code APIException} 异常
@ExceptionHandler({APIException.class})
public Result ? handleBusinessException(APIException ex) {
return Result.failed(ex.getMessage());
* {@code @RequestBody} 参数校验不通过时抛出的异常处理
@ExceptionHandler({MethodArgumentNotValidException.class})
public Result ? handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
StringBuilder sb = new StringBuilder("校验失败:");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
String msg = sb.toString();
if (StringUtils.hasText(msg)) {
return Result.failed(new IResult() {
@Override
public Integer getCode() {
return ResultEnum.VALIDATE_FAILED.getCode();
@Override
public String getMessage() {
return msg;
return Result.failed(ResultEnum.VALIDATE_FAILED);
* {@code @PathVariable} 和 {@code @RequestParam} 参数校验不通过时抛出的异常处理
@ExceptionHandler({ConstraintViolationException.class})
public Result ? handleConstraintViolationException(ConstraintViolationException ex) {
if (StringUtils.hasText(ex.getMessage())) {
return Result.failed(ResultEnum.VALIDATE_FAILED.getCode(), ex.getMessage());
return Result.failed(ResultEnum.VALIDATE_FAILED);
* 顶级异常捕获并统一处理,当其他异常无法处理时候选择使用
@ExceptionHandler({Exception.class})
public Result ? handle(Exception ex) {
return Result.failed(ex.getMessage());
使用hibernate-validator,@Validated注解参数校验
!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --
dependency
groupId org.hibernate /groupId
artifactId hibernate-validator /artifactId
version 6.2.5.Final /version
/dependency
@RequestBody 参数校验
Post、Put 请求的参数推荐使用 @RequestBody 请求体参数。
对 @RequestBody 参数进行校验需要在对象中加入校验条件后,再搭配 @Validated 即可完成自动校验。
UserDTO
package com.wanqi.pojo;
import org.hibernate.validator.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Validated
@RestController
public class TestController {
// 使用@Valid或者@Validated
@PostMapping("/user")
public UserDTO user(@RequestBody @Validated UserDTO userDTO) {
return userDTO;
@PathVariable 和 @RequestParam 参数校验
Get 请求的参数接收一般依赖这两个注解,但是处于 url 有长度限制和代码的可维护性,超过 5 个参数尽量用实体来传参。
对 @PathVariable 和 @RequestParam 参数进行校验需要在入参声明约束的注解。
controller
package com.wanqi.controller;
import com.wanqi.pojo.TestDTO;
import com.wanqi.pojo.UserDTO;
import org.hibernate.validator.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Validated
@RestController
public class TestController {
@GetMapping("/{num}")
public Integer detail(@PathVariable("num") @Min(value = 1 ,message = "不能小于1") @Max(20) Integer num) {
return num * num;
@GetMapping("/getByEmail")
public String getByAccount(@RequestParam @NotBlank @Email String email) {
return email;
自定义校验规则
自定义校验规则需要做两件事情:
自定义注解类,定义错误信息和一些其他需要的内容
注解校验器,定义判定规则
自定义注解类
package com.wanqi.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = MobileValidator.class)
public @interface Mobile {
* 是否允许为空
boolean required() default true;
* 校验不通过返回的提示信息
String message() default "不是一个手机号码格式";
* Constraint要求的属性,用于分组校验和扩展,留空就好
Class ? [] groups() default {};
Class ? extends Payload [] payload() default {};
注解校验器,实现ConstraintValidator接口
package com.wanqi.validator;
import org.springframework.util.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MobileValidator implements ConstraintValidator Mobile, CharSequence {
private boolean required = false;
private final Pattern pattern = Pattern.compile("^1[34578][0-9]{9}$"); // 验证手机号
* 在验证开始前调用注解里的方法,从而获取到一些注解里的参数
* @param constraintAnnotation annotation instance for a given constraint declaration
@Override
public void initialize(Mobile constraintAnnotation) {
this.required = constraintAnnotation.required();
* 判断参数是否合法
* @param value object to validate
* @param context context in which the constraint is evaluated
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (this.required) {
// 验证
return isMobile(value);
if (StringUtils.hasText(value)) {
// 验证
return isMobile(value);
return true;
private boolean isMobile(final CharSequence str) {
Matcher m = pattern.matcher(str);
return m.matches();
package com.wanqi.controller;
import org.hibernate.validator.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
以上就是如何写出优雅的Controller层代码(controller代码怎么写)的详细内容,想要了解更多 如何写出优雅的Controller层代码的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。