一.表单验证(@Valid)

  • 实体类

    1
    2
    @Min(value = 18, message = "未成年少女禁止入内")
    private Integer age;
  • 控制器类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 添加一个女生
    * @return
    */
    @PostMapping(value = "/girls")
    public Girl girlAdd(@Valid Girl girl, BindingResult bindingResult) {
    if(bindingResult.hasErrors()){
    System.out.println(bindingResult.getFieldError().getDefaultMessage());
    return null;
    }
    girl.setCupSize(girl.getCupSize());
    girl.setAge(girl.getAge());
    return girlRepository.save(girl);
    }

二.使用AOP处理请求(注解方式)

  • execution表达式

    @通知类型(value=“execution(* com.banana.impl…*.*(…))”)

    符号 含义
    execution() 表达式的主体
    第一个" * "符号 返回值的类型任意
    com.banana.impl 切点的包名
    …* 当前包及子包的所有类
    .*(…) 表示任何方法名,括号表示参数,两个点表示任何参数类型
  • 代码示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //例子
    @Aspect
    @Component
    public class HttpAspect {
    @Before("execution(public * com.imooc.controller.GirlController.*(..))")
    public void log(){
    System.out.println("before");
    }
    }
  • 日志处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    @Aspect
    @Component
    public class HttpAspect {

    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut("execution(public * com.imooc.controller.GirlController.*(..))")
    public void log(){

    }

    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    //url
    logger.info("url={}",request.getRequestURL());
    //method
    logger.info("method={}",request.getMethod());
    //ip
    logger.info("ip={}",request.getRemoteAddr());
    //类方法
    logger.info("class_method={}",joinPoint.getSignature().getDeclaringType()+"."+joinPoint.getSignature().getName());
    //参数
    logger.info("args={}", joinPoint.getArgs());
    }

    @AfterReturning(returning = "object",pointcut = "log()")
    public void doAfterReturning(Object object){
    logger.info("response={}",object.toString());
    }
    }

总结:使用AOP(面向切面编程)处理请求,特别是通过注解方式,是在现代软件开程中常见的实践,尤其在Java领域。它能够帮助开发者将关注点(如日志记录、性能监控、安全性、事务管理等)从业务逻辑中分离出来,以提高代码的模块化和可重用性。

三.异常处理

SpringBoot 提供了一个灵活的方式来处理应用中的异常,确保你能为用户提供清晰、一致的错误响应。下面是处理异常的一些常见方法:

3.1. @ControllerAdvice 和 @ExceptionHandler

使用 @ControllerAdvice 类可以实现全局异常处理。这个类可以包含一个或多个用 @ExceptionHandler 注解的方法,用来处理特定的异常类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(value = Exception.class)
public ResponseEntity<Object> handleGeneralException(Exception ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", "Something went wrong");
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}

// 可以添加更多的异常处理方法来处理不同类型的异常
}

3.2. ResponseStatusException

在Spring 5及Spring Boot 2中,可以直接在控制器方法中抛出ResponseStatusException。这是处理异常的一种快速方法,允许你直接在代码中指定HTTP状态码和错误消息。

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class SomeController {

@GetMapping("/somePath")
public String someMethod() {
if(someConditionNotMet) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Entity not found");
}
return "Some response";
}
}

3.3. 使用 @ResponseStatus 在异常类上

你可以创建自定义异常,并使用 @ResponseStatus 注解来指示当异常被抛出时应该返回哪个HTTP状态码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}

@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFoundException ex) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
}
}

3.4. BasicErrorController

SpringBoot默认提供了BasicErrorController来处理所有的错误路径。如果上述方法不适合你的需求,你可以通过扩展BasicErrorController或覆盖其方法来自定义错误处理逻辑。

3.5. Error Handling for RESTful Services

对于RESTful服务,你可能希望返回JSON或XML格式的错误响应。上述的@ControllerAdvice可以很方便地实现这一点,通过设置响应体和HTTP状态码,来提供详细的错误信息给客户端。

这些方法提供了强大且灵活的异常处理机制,你可以根据项目的需要选择最适合的方式。

四.单元测试

  • 测试Service

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class GirlServiceTest {

    @Autowired
    private GirlService girlService;

    @Test
    public void Test(){
    ...
    Assert.assertEquals(...);
    }
    }
  • 测试API

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @RunWith(SpringRunner.class)    
    @SpringBootTest
    @AutoConfigureMockMvc
    public class GirlControllerTest {
    @Autowired
    private MockMvc mvc;

    @Test
    public void girlList() throws Exception{
    mvc.perform(MockMvcRequestBuilders.get("/girls"))
    .andExpect(MockMvcResultMatchers.status().isOk())
    .andExpect(MockMvcResultMatchers.content().string("abc..."));
    }
    }