本篇文章为大家展示了使用Spring MVC怎么对参数进行校验,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
1. 内嵌异常处理
如果只是这个controller的异常做单独处理,那么就适合绑定这个controller本身的异常。
具体做法是使用注解@ExceptionHandler.
在这个controller中添加一个方法,并添加上述注解,并指明要拦截的异常。
@RequestMapping(value = "saveOrUpdate", method = RequestMethod.POST)public String saveOrUpdate(HttpServletResponse response, @RequestBody Order order){ CodeMsg result = null; try { result = orderService.saveOrUpdate(order); } catch (Exception e) { logger.error("save failed.", e); return this.renderString(response, CodeMsg.error(e.getMessage())); } return this.renderString(response, result);}@ResponseBody@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(HttpMessageNotReadableException.class)public CodeMsg messageNotReadable(HttpMessageNotReadableException exception, HttpServletResponse response){ LOGGER.error("请求参数不匹配。", exception); return CodeMsg.error(exception.getMessage());}
这里saveOrUpdate是我们想要拦截一样的请求,而messageNotReadable则是处理异常的代码。
@ExceptionHandler(HttpMessageNotReadableException.class)表示我要拦截何种异常。在这里,由于springmvc默认采用jackson作为json序列化工具,当反序列化失败的时候就会抛出HttpMessageNotReadableException异常。具体如下:
{ "code": 1, "msg": "Could not read JSON: Failed to parse Date value '2017-03-' (format: \"yyyy-MM-dd HH:mm:ss\"): Unparseable date: \"2017-03-\" (through reference chain: com.test.modules.order.entity.Order[\"serveTime\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to parse Date value '2017-03-' (format: \"yyyy-MM-dd HH:mm:ss\"): Unparseable date: \"2017-03-\" (through reference chain: com.test.modules.order.entity.Order[\"serveTime\"])", "data": ""}
这是个典型的jackson反序列化失败异常,也是造成我遇见过的400原因最多的。通常是日期格式不对。
另外, @ResponseStatus(HttpStatus.BAD_REQUEST)
这个注解是为了标识这个方法返回值的HttpStatus code。我设置为400,当然也可以自定义成其他的。
2. 批量异常处理
看到大多数资料写的是全局异常处理,我觉得对我来说批量更合适些,因为我只是希望部分controller被拦截而不是全部。
springmvc提供了@ControllerAdvice来做批量拦截。
第一次看到注释这么少的源码,忍不住多读几遍。
Indicates the annotated class assists a "Controller".
表示这个注解是服务于Controller的。
Serves as a specialization of {@link Component @Component}, allowing for implementation classes to be autodetected through classpath scanning.
用来当做特殊的Component注解,允许使用者扫描发现所有的classpath。
It is typically used to define {@link ExceptionHandler @ExceptionHandler}, * {@link InitBinder @InitBinder}, and {@link ModelAttribute @ModelAttribute} * methods that apply to all {@link RequestMapping @RequestMapping} methods.
典型的应用是用来定义xxxx.
One of {@link #annotations()}, {@link #basePackageClasses()}, * {@link #basePackages()} or its alias {@link #value()} * may be specified to define specific subsets of Controllers * to assist. When multiple selectors are applied, OR logic is applied - * meaning selected Controllers should match at least one selector.
这几个参数指定了扫描范围。
the default behavior (i.e. if used without any selector), * the {@code @ControllerAdvice} annotated class will * assist all known Controllers.
默认扫描所有的已知的的Controllers。
Note that those checks are done at runtime, so adding many attributes and using * multiple strategies may have negative impacts (complexity, performance).
注意这个检查是在运行时做的,所以注意性能问题,不要放太多的参数。
说的如此清楚,以至于用法如此简单。
@ResponseBody@ControllerAdvice("com.api")public class ApiExceptionHandler extends BaseClientController { private static final Logger LOGGER = LoggerFactory.getLogger(ApiExceptionHandler.class); @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(UnexpectedTypeException.class) public CodeMsg unexpectedType(UnexpectedTypeException exception, HttpServletResponse response){ LOGGER.error("校验方法太多,不确定合适的校验方法。", exception); return CodeMsg.error(exception.getMessage()); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public CodeMsg messageNotReadable(HttpMessageNotReadableException exception, HttpServletResponse response){ LOGGER.error("请求参数不匹配,request的json格式不正确", exception); return CodeMsg.error(exception.getMessage()); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(Exception.class) public CodeMsg ex(MethodArgumentNotValidException exception, HttpServletResponse response){ LOGGER.error("请求参数不合法。", exception); BindingResult bindingResult = exception.getBindingResult(); String msg = "校验失败"; return new CodeMsg(CodeMsgConstant.error, msg, getErrors(bindingResult)); } private Map<String, String> getErrors(BindingResult result) { Map<String, String> map = new HashMap<>(); List<FieldError> list = result.getFieldErrors(); for (FieldError error : list) { map.put(error.getField(), error.getDefaultMessage()); } return map; }}
3. Hibernate-validate
使用参数校验如果不catch异常就会返回400. 所以这个也要规范一下。
1 引入hibernate-validate
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.0.2.Final</version> </dependency>
<mvc:annotation-driven validator="validator" /><bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/> <property name="validationMessageSource" ref="messageSource"/></bean>
2 使用
在实体类字段上标注要求
public class AlipayRequest { @NotEmpty private String out_trade_no; private String subject; @DecimalMin(value = "0.01", message = "费用最少不能小于0.01") @DecimalMax(value = "100000000.00", message = "费用最大不能超过100000000") private String total_fee; @NotEmpty(message = "订单类型不能为空") private String business_type; //....}
controller里添加@Valid
@RequestMapping(value = "sign", method = RequestMethod.POST) public String sign(@Valid @RequestBody AlipayRequest params ){ .... }
4.错误处理
前面已经提到,如果不做处理的结果就是400,415. 这个对应Exception是MethodArgumentNotValidException,也是这样:
@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler(Exception.class)public CodeMsg ex(MethodArgumentNotValidException exception, HttpServletResponse response){ LOGGER.error("请求参数不合法。", exception); BindingResult bindingResult = exception.getBindingResult(); String msg = "校验失败"; return new CodeMsg(CodeMsgConstant.error, msg, getErrors(bindingResult));}private Map<String, String> getErrors(BindingResult result) { Map<String, String> map = new HashMap<>(); List<FieldError> list = result.getFieldErrors(); for (FieldError error : list) { map.put(error.getField(), error.getDefaultMessage()); } return map;}
返回结果:
{ "code": 1, "msg": "校验失败", "data": { "out_trade_no": "不能为空", "business_type": "订单类型不能为空" }}
大概有这么几个限制注解:
上述内容就是使用Spring MVC怎么对参数进行校验,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。