最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring MVC參數(shù)校驗(yàn)詳解(關(guān)于`@RequestBody`返回`400`)

 更新時(shí)間:2017年08月04日 10:33:29   作者:風(fēng)  
這篇文章主要介紹了Spring MVC參數(shù)校驗(yàn)的相關(guān)資料,主要是針對(duì)`@RequestBody`返回`400`的問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。

前言

工作中發(fā)現(xiàn)一個(gè)定律,如果總是習(xí)慣別人幫忙做事的結(jié)果是自己不會(huì)做事了。一直以來,spring幫我解決了程序運(yùn)行中的各種問題,我只要關(guān)心我的業(yè)務(wù)邏輯,設(shè)計(jì)好我的業(yè)務(wù)代碼,返回正確的結(jié)果即可。直到遇到了400。

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

springmvc異常處理

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

1. 內(nèi)嵌異常處理

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

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

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

@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("請(qǐng)求參數(shù)不匹配。", exception);
 return CodeMsg.error(exception.getMessage());
}

這里saveOrUpdate是我們想要攔截一樣的請(qǐng)求,而messageNotReadable則是處理異常的代碼。
@ExceptionHandler(HttpMessageNotReadableException.class)表示我要攔截何種異常。在這里,由于springmvc默認(rèn)采用jackson作為json序列化工具,當(dāng)反序列化失敗的時(shí)候就會(huì)拋出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": ""
}

這是個(gè)典型的jackson反序列化失敗異常,也是造成我遇見過的400原因最多的。通常是日期格式不對(duì)。

另外, @ResponseStatus(HttpStatus.BAD_REQUEST)這個(gè)注解是為了標(biāo)識(shí)這個(gè)方法返回值的HttpStatus code。我設(shè)置為400,當(dāng)然也可以自定義成其他的。

2. 批量異常處理

看到大多數(shù)資料寫的是全局異常處理,我覺得對(duì)我來說批量更合適些,因?yàn)槲抑皇窍M糠謈ontroller被攔截而不是全部。

springmvc提供了@ControllerAdvice來做批量攔截。

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

Indicates the annotated class assists a "Controller".

表示這個(gè)注解是服務(wù)于Controller的。

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

用來當(dāng)做特殊的Component注解,允許使用者掃描發(fā)現(xiàn)所有的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.

典型的應(yīng)用是用來定義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.

這幾個(gè)參數(shù)指定了掃描范圍。

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

默認(rèn)掃描所有的已知的的Controllers。

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

注意這個(gè)檢查是在運(yùn)行時(shí)做的,所以注意性能問題,不要放太多的參數(shù)。

說的如此清楚,以至于用法如此簡(jiǎn)單。

@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("校驗(yàn)方法太多,不確定合適的校驗(yàn)方法。", exception);
  return CodeMsg.error(exception.getMessage());
 }
 @ResponseStatus(HttpStatus.BAD_REQUEST)
 @ExceptionHandler(HttpMessageNotReadableException.class)
 public CodeMsg messageNotReadable(HttpMessageNotReadableException exception, HttpServletResponse response){
  LOGGER.error("請(qǐng)求參數(shù)不匹配,request的json格式不正確", exception);
  return CodeMsg.error(exception.getMessage());
 }
 @ResponseStatus(HttpStatus.BAD_REQUEST)
 @ExceptionHandler(Exception.class)
 public CodeMsg ex(MethodArgumentNotValidException exception, HttpServletResponse response){
  LOGGER.error("請(qǐng)求參數(shù)不合法。", exception);
  BindingResult bindingResult = exception.getBindingResult();
  String msg = "校驗(yàn)失敗";
  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

使用參數(shù)校驗(yàn)如果不catch異常就會(huì)返回400. 所以這個(gè)也要規(guī)范一下。

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 使用

在實(shí)體類字段上標(biāo)注要求

public class AlipayRequest {
 @NotEmpty
 private String out_trade_no;
 private String subject;
 @DecimalMin(value = "0.01", message = "費(fèi)用最少不能小于0.01")
 @DecimalMax(value = "100000000.00", message = "費(fèi)用最大不能超過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.錯(cuò)誤處理

前面已經(jīng)提到,如果不做處理的結(jié)果就是400,415. 這個(gè)對(duì)應(yīng)Exception是MethodArgumentNotValidException,也是這樣:

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(Exception.class)
public CodeMsg ex(MethodArgumentNotValidException exception, HttpServletResponse response){
 LOGGER.error("請(qǐng)求參數(shù)不合法。", exception);
 BindingResult bindingResult = exception.getBindingResult();
 String msg = "校驗(yàn)失敗";
 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;
}

返回結(jié)果:

{
 "code": 1,
 "msg": "校驗(yàn)失敗",
 "data": {
 "out_trade_no": "不能為空",
 "business_type": "訂單類型不能為空"
 }
}

大概有這么幾個(gè)限制注解:

/**
 * Bean Validation 中內(nèi)置的 constraint  
 * @Null 被注釋的元素必須為 null  
 * @NotNull 被注釋的元素必須不為 null  
 * @AssertTrue  被注釋的元素必須為 true  
 * @AssertFalse 被注釋的元素必須為 false  +
 * @Min(value)  被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值  
 * @Max(value)  被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值  
 * @DecimalMin(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值  
 * @DecimalMax(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值  
 * @Size(max=, min=) 被注釋的元素的大小必須在指定的范圍內(nèi)  
 * @Digits (integer, fraction)  被注釋的元素必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi)  
 * @Past 被注釋的元素必須是一個(gè)過去的日期  
 * @Future  被注釋的元素必須是一個(gè)將來的日期  
 * @Pattern(regex=,flag=) 被注釋的元素必須符合指定的正則表達(dá)式  
 * Hibernate Validator 附加的 constraint  
 * @NotBlank(message =) 驗(yàn)證字符串非null,且長(zhǎng)度必須大于0  
 * @Email 被注釋的元素必須是電子郵箱地址  
 * @Length(min=,max=) 被注釋的字符串的大小必須在指定的范圍內(nèi)  
 * @NotEmpty 被注釋的字符串的必須非空  
 * @Range(min=,max=,message=) 被注釋的元素必須在合適的范圍內(nèi) 
 */

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • java使用hashMap緩存保存數(shù)據(jù)的方法

    java使用hashMap緩存保存數(shù)據(jù)的方法

    這篇文章主要介紹了java使用hashMap緩存保存數(shù)據(jù)的方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了java基于hashmap讀寫緩存數(shù)據(jù)的相關(guān)操作技巧,需要的朋友可以參考下
    2016-08-08
  • Java調(diào)用pyzbar解析base64二維碼過程解析

    Java調(diào)用pyzbar解析base64二維碼過程解析

    這篇文章主要介紹了Java調(diào)用pyzbar解析base64二維碼過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringMVC4.3解析器HandlerMethodArgumentResolver接口源碼

    SpringMVC4.3解析器HandlerMethodArgumentResolver接口源碼

    這篇文章主要為大家介紹了SpringMVC4.3解析器HandlerMethodArgumentResolver接口源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Spring Session的使用示例

    Spring Session的使用示例

    最近團(tuán)隊(duì)一個(gè)項(xiàng)目需要使用Session,之前沒有在實(shí)際項(xiàng)目中使用過Spring-Session,這里記錄一下使用的過程
    2021-06-06
  • 如何使用Bean Validation 解決業(yè)務(wù)中參數(shù)校驗(yàn)

    如何使用Bean Validation 解決業(yè)務(wù)中參數(shù)校驗(yàn)

    這篇文章主要介紹了如何使用Bean Validation 解決業(yè)務(wù)中參數(shù)校驗(yàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 基于selenium 獲取新頁(yè)面元素失敗的解決方法

    基于selenium 獲取新頁(yè)面元素失敗的解決方法

    今天小編就為大家分享一篇基于selenium 獲取新頁(yè)面元素失敗的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • java使用jacob實(shí)現(xiàn)word轉(zhuǎn)pdf

    java使用jacob實(shí)現(xiàn)word轉(zhuǎn)pdf

    這篇文章主要為大家詳細(xì)介紹了java使用jacob實(shí)現(xiàn)word轉(zhuǎn)pdf,通過調(diào)用模板文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Jar包反編譯修改代碼后重新打jar包圖文詳解

    Jar包反編譯修改代碼后重新打jar包圖文詳解

    這篇文章主要給大家介紹了關(guān)于Jar包反編譯修改代碼后重新打jar包的相關(guān)資料,JAR文件就是Java Archive(Java檔案文件),它是 Java的一種文檔格式,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Java實(shí)現(xiàn)二分查找樹及其相關(guān)操作

    Java實(shí)現(xiàn)二分查找樹及其相關(guān)操作

    二分查找樹是一種有組織的二叉樹。我們可以通過鏈接節(jié)點(diǎn)表示這樣一棵樹,二分查找樹(Binary Search Tree)的基本操作有搜索、求最大值、求最小值、求前驅(qū)、求后繼、插入及刪除,對(duì)java二分查找樹相關(guān)知識(shí)感興趣的朋友一起看看吧
    2021-07-07
  • Java中StringBuilder與StringBuffer的區(qū)別

    Java中StringBuilder與StringBuffer的區(qū)別

    在Java編程中,字符串的拼接是一項(xiàng)常見的操作。為了有效地處理字符串的拼接需求,Java提供了兩個(gè)主要的類:StringBuilder和StringBuffer,本文主要介紹了Java中StringBuilder與StringBuffer的區(qū)別,感興趣的可以了解一下
    2023-08-08

最新評(píng)論

长岭县| 聂拉木县| 松滋市| 商南县| 平江县| 吕梁市| 翁牛特旗| 红河县| 襄垣县| 石首市| 长顺县| 金湖县| 巫溪县| 托里县| 盘山县| 大丰市| 武城县| 富宁县| 拉萨市| 营口市| 龙海市| 兴仁县| 汶川县| 遵义市| 广安市| 改则县| 平和县| 集贤县| 沙河市| 酒泉市| 富宁县| 岳阳县| 汝阳县| 来安县| 南皮县| 伊吾县| 铜梁县| 缙云县| 晋中市| 乐山市| 平舆县|