前言

工作中发现一个定律,如果总是习惯别人帮忙做事的结果是自己不会做事了。一直以来,spring帮我解决了程序运行中的各种问题,我只要关心我的业务逻辑,设计好我的业务代码,返回正确的结果即可。直到遇到了400。
spring返回400的时候通常没有任何错误提示,当然也通常是参数不匹配。这在参数少的情况下还可以一眼看穿,但当参数很大是,排除参数也很麻烦,更何况,既然错误了,为什么指出来原因呢。好吧,springmvc把这个权力交给了用户自己。话不多说了,来一起看看详细的介绍吧。
springmvc异常处理
最开始的时候也想过自己拦截会出异常的method来进行异常处理,但显然不需要这么做。spring提供了内嵌的以及全局的异常处理方法,基本可以满足我的需求了。
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);
/**
*
* @param exception UnexpectedTypeException
* @param response
* @return
*/
@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. 所以这个也要规范一下。
3.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>
3.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": "订单类型不能为空"
}
}
大概有这么几个限制注解:
/** * Bean Validation 中内置的 constraint * @Null 被注释的元素必须为 null * @NotNull 被注释的元素必须不为 null * @AssertTrue 被注释的元素必须为 true * @AssertFalse 被注释的元素必须为 false + * @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 * @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 * @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 * @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 * @Size(max=, min=) 被注释的元素的大小必须在指定的范围内 * @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内 * @Past 被注释的元素必须是一个过去的日期 * @Future 被注释的元素必须是一个将来的日期 * @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式 * Hibernate Validator 附加的 constraint * @NotBlank(message =) 验证字符串非null,且长度必须大于0 * @Email 被注释的元素必须是电子邮箱地址 * @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内 * @NotEmpty 被注释的字符串的必须非空 * @Range(min=,max=,message=) 被注释的元素必须在合适的范围内 */
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
# springmvc
# 参数校验
# springmvc的参数校验
# springmvc校验
# 使用SpringMVC访问Controller接口返回400BadRequest
# springmvc 获取@Requestbody转换的异常处理方式
# SpringMVC @RequestBody出现400 Bad Req
# 是一个
# 就会
# 太多
# 为空
# 不匹配
# 序列化
# 内嵌
# 不合法
# 的是
# 最小值
# 几个
# 我要
# 是在
# 在这里
# 在这个
# 我觉得
# 还可以
# 最多
# 也要
# 不需要
相关文章:
如何在建站主机中优化服务器配置?
建站之星与建站宝盒如何选择最佳方案?
公司网站建设制作费用,想建设一个属于自己的企业网站,该如何去做?
香港服务器租用每月最低只需15元?
h5在线制作网站电脑版下载,h5网页制作软件?
如何解决ASP生成WAP建站中文乱码问题?
专业商城网站制作公司有哪些,pi商城官网是哪个?
如何用PHP工具快速搭建高效网站?
小程序网站制作需要准备什么资料,如何制作小程序?
广州商城建站系统开发成本与周期如何控制?
如何在阿里云服务器自主搭建网站?
合肥做个网站多少钱,合肥本地有没有比较靠谱的交友平台?
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
高防服务器租用指南:配置选择与快速部署攻略
武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?
小建面朝正北,A点实际方位是否存在偏差?
如何构建满足综合性能需求的优质建站方案?
制作农业网站的软件,比较好的农业网站推荐一下?
制作企业网站建设方案,怎样建设一个公司网站?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
手机网站制作与建设方案,手机网站如何建设?
如何确保西部建站助手FTP传输的安全性?
香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化
GML (Geography Markup Language)是什么,它如何用XML来表示地理空间信息?
如何高效生成建站之星成品网站源码?
枣阳网站制作,阳新火车站打的到仙岛湖多少钱?
为什么Go需要go mod文件_Go go mod文件作用说明
C#如何使用XPathNavigator高效查询XML
如何通过商城自助建站源码实现零基础高效建站?
广州建站公司哪家好?十大优质服务商推荐
教学论文网站制作软件有哪些,写论文用什么软件
?
建站之星代理如何获取技术支持?
阿里云网站制作公司,阿里云快速搭建网站好用吗?
南阳网站制作公司推荐,小学电子版试卷去哪里找资源好?
盐城做公司网站,江苏电子版退休证办理流程?
高端建站如何打造兼具美学与转化的品牌官网?
如何在宝塔面板创建新站点?
文字头像制作网站推荐软件,醒图能自动配文字吗?
如何在Golang中使用replace替换模块_指定本地或远程路径
如何快速启动建站代理加盟业务?
简易网站制作视频教程,使用记事本编写一个简单的网页html文件?
宝塔建站无法访问?如何排查配置与端口问题?
Python文件管理规范_工程实践说明【指导】
建站中国官网:模板定制+SEO优化+建站流程一站式指南
C++如何使用std::optional?(处理可选值)
如何批量查询域名的建站时间记录?
如何用搬瓦工VPS快速搭建个人网站?
网站制作壁纸教程视频,电脑壁纸网站?
如何确保FTP站点访问权限与数据传输安全?
上海网站制作开发公司,上海买房比较好的网站有哪些?
*请认真填写需求信息,我们会在24小时内与您取得联系。