一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring MVC參數校驗詳解(關于`@RequestBody`返回`400`)

Spring MVC參數校驗詳解(關于`@RequestBody`返回`400`)

2020-12-10 14:37 Java教程

這篇文章主要介紹了Spring MVC參數校驗的相關資料,主要是針對`@RequestBody`返回`400`的問題,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面跟著小編來一起學習學習吧。

前言

工作中發現一個定律,如果總是習慣別人幫忙做事的結果是自己不會做事了。一直以來,spring幫我解決了程序運行中的各種問題,我只要關心我的業務邏輯,設計好我的業務代碼,返回正確的結果即可。直到遇到了400。

spring返回400的時候通常沒有任何錯誤提示,當然也通常是參數不匹配。這在參數少的情況下還可以一眼看穿,但當參數很大是,排除參數也很麻煩,更何況,既然錯誤了,為什么指出來原因呢。好吧,springmvc把這個權力交給了用戶自己。話不多說了,來一起看看詳細的介紹吧。

springmvc異常處理

最開始的時候也想過自己攔截會出異常的method來進行異常處理,但顯然不需要這么做。spring提供了內嵌的以及全局的異常處理方法,基本可以滿足我的需求了。

1. 內嵌異常處理

如果只是這個controller的異常做單獨處理,那么就適合綁定這個controller本身的異常。

具體做法是使用注解@ExceptionHandler.

在這個controller中添加一個方法,并添加上述注解,并指明要攔截的異常。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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異常。具體如下:

?
1
2
3
4
5
{
 "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來做批量攔截。

第一次看到注釋這么少的源碼,忍不住多讀幾遍。

?
1
Indicates the annotated class assists a "Controller".

表示這個注解是服務于Controller的。

?
1
Serves as a specialization of {@link Component @Component}, allowing for implementation classes to be autodetected through classpath scanning.

用來當做特殊的Component注解,允許使用者掃描發現所有的classpath。

?
1
2
3
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.

?
1
2
3
4
5
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.

這幾個參數指定了掃描范圍。

?
1
2
3
the default behavior (i.e. if used without any selector),
 * the {@code @ControllerAdvice} annotated class will
 * assist all known Controllers.

默認掃描所有的已知的的Controllers。

?
1
2
Note that those checks are done at runtime, so adding many attributes and using
 * multiple strategies may have negative impacts (complexity, performance).

注意這個檢查是在運行時做的,所以注意性能問題,不要放太多的參數。

說的如此清楚,以至于用法如此簡單。

?
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
34
35
36
37
38
39
@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

?
1
2
3
4
5
<dependency>
 <groupId>org.hibernate</groupId>
 <artifactId>hibernate-validator</artifactId>
 <version>5.0.2.Final</version>
</dependency>
?
1
2
3
4
5
<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 使用

在實體類字段上標注要求

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

?
1
2
3
4
5
@RequestMapping(value = "sign", method = RequestMethod.POST)
 public String sign(@Valid @RequestBody AlipayRequest params
 ){
  ....
 }

4.錯誤處理

前面已經提到,如果不做處理的結果就是400,415. 這個對應Exception是MethodArgumentNotValidException,也是這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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;
}

返回結果:

?
1
2
3
4
5
6
7
8
{
 "code": 1,
 "msg": "校驗失敗",
 "data": {
 "out_trade_no": "不能為空",
 "business_type": "訂單類型不能為空"
 }
}

大概有這么幾個限制注解:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * 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=) 被注釋的元素必須在合適的范圍內
 */

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://ryan-miao.github.io/2017/05/20/spring400/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 加勒比伊人 | 国产小视频在线免费 | 女人扒开下面让男人桶爽视频 | 国产精品免费综合一区视频 | 男人机机桶女人 | 大乳一级一区二区三区 | 新影音先锋男人色资源网 | yellow视频在线观看 | 国产精品日韩欧美一区二区三区 | 国产精品视频在这里有精品 | 色狠狠色狠狠综合天天 | julia ann一hd| 欧美一级欧美三级 | 精品国产免费久久久久久 | 免费在线看a| 性欧美高清强烈性视频 | 四虎影院免费在线 | 美女翘臀跪床被打屁股作文 | 无人区国产大片 | 婷婷久久综合九色综合九七 | 国产午夜精品久久理论片小说 | 99re在线视频免费观看 | 亚洲欧美日韩中文字幕久久 | 小sao货ji巴cao死你视频 | 精品久久久噜噜噜久久7 | 国产精品久久久久久久久免费 | 边打电话边操 | 国产农村一一级特黄毛片 | 毛片免费视频观看 | 操一操影院| 国产欧美日韩视频在线观看一区二区 | 脱了白丝校花的内裤猛烈进入 | 我将她侵犯1~6樱花动漫在线看 | 欧美视频在线播放观看免费福利资源 | 亚洲精品乱码久久久久久蜜桃 | 91国产在线视频 | 三上悠亚国产精品一区 | kk4444在线影视播放 | 9966国产精品视频 | 亚洲欧美日韩综合在线播放 | 色哟哟国产成人精品 |