使用@Valid+BindingResult進(jìn)行controller參數(shù)校驗(yàn)方式
@Valid+BindingResult進(jìn)行controller參數(shù)校驗(yàn)
由于controller是調(diào)用的第一層,經(jīng)常參數(shù)校驗(yàn)將在這里完成,常見有非空校驗(yàn)、類型校驗(yàn)等,常見寫法為以下偽代碼:
public void round(Object a){
if(a.getLogin() == null){
return "手機(jī)號不能為空!";
}
}
但是調(diào)用對象的位置會(huì)有很多,而且手機(jī)號都不能為空,那么我們會(huì)想到把校驗(yàn)方法抽出來,避免重復(fù)的代碼。但有框架支持我們通過注解的方式進(jìn)行參數(shù)校驗(yàn)。
先立個(gè)場景,為往動(dòng)物園添加動(dòng)物,動(dòng)物對象如下,時(shí)間節(jié)點(diǎn)大概在3030年,我們認(rèn)為動(dòng)物可登陸動(dòng)物專用的系統(tǒng),所以有password即自己的登錄密碼。
public class Animal {
private String name;
private Integer age;
private String password;
private Date birthDay;
}
調(diào)用創(chuàng)建動(dòng)物的controller層如下,簡潔明了,打印下信息后直接返回。
@RestController
@RequestMapping("/animal")
public class AnimalController {
@PostMapping
public Animal createAnimal(@RequestBody Animal animal){
logger.info(animal.toString());
return animal;
}
}
偽造Mvc調(diào)用的測試類。
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAnimal {
private final static Logger logger = LoggerFactory.getLogger(TestAnimal.class);
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void initMock(){
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void createAnimal() throws Exception {
String content = "{\"name\":\"elephant\",\"password\":null,\"birthDay\":"+System.currentTimeMillis()+"}";
String result = mockMvc.perform(MockMvcRequestBuilders.post("/animal")
.content(content)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn().getResponse().getContentAsString();
logger.info(result);
}
}
以上代碼基于搭建的springboot項(xiàng)目,想搭建的同學(xué)可以參考搭建篇 http://www.dhdzp.com/article/226998.htm
代碼分析,日期格式的參數(shù)建議使用時(shí)間戳傳遞,以上birthDay傳遞 "2018-05-08 20:00:00",將會(huì)拋出日期轉(zhuǎn)換異常,感興趣的同學(xué)可以試試。
由于密碼很重要,現(xiàn)在要求密碼為必填,操作如下,添加@NotBlank注解到password上:
@NotBlank private String password;
但光加校驗(yàn)注解是不起作用的,還需要在方法參數(shù)上添加@Valid注解,如下:
@Valid @RequestBody Animal animal
此時(shí)執(zhí)行測試方法,拋出異常,返回狀態(tài)為400:
java.lang.AssertionError: Status
Expected :200
Actual :400
<Click to see difference>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81)
說明對password的非空校驗(yàn)已經(jīng)生效了,直接拋出異常。如果不想拋出異常,想返回校驗(yàn)信息給前端,這個(gè)時(shí)候就需要用到BindingResult了,修改創(chuàng)建動(dòng)物的方法,添加BindingResult參數(shù):
@PostMapping
public Animal createAnimal(@Valid @RequestBody Animal animal, BindingResult bindingResult){
if (bindingResult.hasErrors()){
bindingResult.getAllErrors().forEach(o ->{
FieldError error = (FieldError) o;
logger.info(error.getField() + ":" + error.getDefaultMessage());
});
}
logger.info(animal.toString());
return animal;
}
此時(shí),執(zhí)行測試,可以看到日志中的錯(cuò)誤信息:
2018-05-09 00:59:37.453 INFO 14044 --- [ main] c.i.s.d.web.controller.AnimalController : password:may not be empty
為了滿足我們編碼需要我們需要進(jìn)行代碼改造,1.不能直接返回animal。2.返回的提示信息得是用戶可讀懂的信息。
controller方法改造如下,通過Map對象傳遞請求成功后的信息或錯(cuò)誤提示信息。
@PostMapping
public Map<String,Object> createAnimal(@Valid @RequestBody Animal animal, BindingResult bindingResult){
logger.info(animal.toString());
Map<String,Object> result = new HashMap<>();
if (bindingResult.hasErrors()){
FieldError error = (FieldError) bindingResult.getAllErrors().get(0);
result.put("code","400");//錯(cuò)誤編碼400
result.put("message",error.getDefaultMessage());//錯(cuò)誤信息
return result;
}
result.put("code","200");//成功編碼200
result.put("data",animal);//成功返回?cái)?shù)據(jù)
return result;
}
返回的密碼提示信息如下:
@NotBlank(message = "密碼不能為空!") private String password;
執(zhí)行測試方法,返回結(jié)果
com.imooc.security.demo.TestAnimal : {"code":"400","message":"密碼不能為空!"}
最后貼一個(gè),設(shè)置password值返回成功的信息
com.imooc.security.demo.TestAnimal : {"code":"200","data":{"name":"elephant","age":null,"password":"lalaland","birthDay":1525799768955}}
由于篇幅有限,下次會(huì)以這個(gè)實(shí)例為基礎(chǔ),實(shí)現(xiàn)一個(gè)自定義的注解實(shí)現(xiàn),該篇文章到此結(jié)束。
Controller層方法的參數(shù)校驗(yàn)
import com.example.demo.pojo.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
@Controller
@RequestMapping("/stu")
public class StudentController {
@PostMapping("/addStu")
@ResponseBody
public String addStudent(@Valid Student student){
System.out.println("存儲student對象");
System.out.println(student);
return "ok";
}
}
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class Student {
@NotNull(message = "傳入的是空值,請傳值")
@Min(value = 0,message = "傳入學(xué)生分?jǐn)?shù)有誤,分?jǐn)?shù)在0-100之間")
@Max(value = 100,message = "傳入學(xué)生分?jǐn)?shù)有誤,分?jǐn)?shù)在0-100之間")
private Integer score;
@NotEmpty(message = "傳入的是空字符串,請傳值")
@NotNull(message = "傳入的是空值,請傳值")
private String name;
@NotNull(message = "傳入的是空值,請傳值")
@NotEmpty(message = "傳入的是空字符串,請傳值")
@Length(min = 11,max = 11,message = "號碼有誤,長度應(yīng)為11位")
private String mobile;
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
@Override
public String toString() {
return "Student{" +
"score=" + score +
", name='" + name + '\'' +
", mobile='" + mobile + '\'' +
'}';
}
}
全局統(tǒng)一異常攔截器
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionInterceptor {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String exceptionHandler(Exception e){
String failMessage=null;
if(e instanceof BindException){
failMessage=((BindException) e).getBindingResult().getFieldError().getDefaultMessage();
}
return failMessage;
}
}


當(dāng)我們傳入的參數(shù)有誤時(shí),就會(huì)被異常攔截器捕獲,返回給我們錯(cuò)誤信息。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java實(shí)現(xiàn)的日期處理類完整實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)的日期處理類,結(jié)合完整實(shí)例形式分析了Java針對日期的獲取、運(yùn)算、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
java如何根據(jù)PostMan發(fā)送請求設(shè)置接口請求工具類
在Java中調(diào)用第三方接口可以通過不同的方式,如使用GET、POST等請求,關(guān)鍵點(diǎn)包括設(shè)置正確的請求方式、URL、參數(shù)(params)、頭信息(headers)和請求體(body),對于不同的數(shù)據(jù)格式,如XML和JSON,需在header中聲明內(nèi)容類型2024-09-09
SpringBoot中restTemplate請求存在亂碼問題的解決方法
這篇文章主要介紹了SpringBoot中restTemplate請求存在亂碼問題的解決方法,文中有相關(guān)的圖文和代碼示例供大家參考,對大家的解決問題有一定的幫助,需要的朋友可以參考下2024-11-11
Java反轉(zhuǎn)字符串和相關(guān)字符編碼的問題解決
反轉(zhuǎn)字符串一直被當(dāng)作是簡單問題,大家的思想主要就是利用遍歷,首尾交換字符實(shí)現(xiàn)字符串的反轉(zhuǎn)。例如下面的代碼,就可以簡單實(shí)現(xiàn)反轉(zhuǎn)。2013-05-05
Springboot支持Emoji表情的實(shí)現(xiàn)方法
本文主要介紹了Springboot 支持Emoji 表情,本篇的實(shí)現(xiàn)方式是僅需后端處理,具有一定的參考價(jià)值,需要的朋友可以參考一下。2021-07-07
Java實(shí)現(xiàn)經(jīng)典游戲復(fù)雜迷宮
這篇文章主要介紹了如何利用java語言實(shí)現(xiàn)經(jīng)典《復(fù)雜迷宮》游戲,文中采用了swing技術(shù)進(jìn)行了界面化處理,感興趣的小伙伴可以動(dòng)手試一試2022-02-02
自制Java工具實(shí)現(xiàn)翻譯鼠標(biāo)選中文本
這篇文章主要為大家詳細(xì)介紹了如何自制Java工具實(shí)現(xiàn)ctrl+c+c翻譯鼠標(biāo)選中文本,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-01-01

