使用@RestController,@ExceptionHandler和@Valid,把检验和异常处理从主要业务逻辑里面抽离出来
2017-06-08 10:44
417 查看
@Restcontroller登场
spring从4.0开始引入了@Restcontroller,这是对REST的支持,他可以帮我们去掉@ResponseBody这个
所以原本这样的代码,
使用@RestController后
是的,少了一个@ResponseBody这个玩意.感觉也是挺舒服的,如果你这个Controller有几十个方法的话.
关于异常的处理
上面的代码看起来没什么问题.但如果没有这个id呢?返回的内容居然就是个null,那显然不太好.
所以我们需要加点返回信息,最少设置头部的状态为NO_FOUND像下面这样,
如果返回为空,我们返回的状态为HttpStatus.NOT_FOUND,虽然spittle这个为空依然没解决,也没返回什么具体的信息.所以我们该加点内容,例如msg为改ID无效之类的.
Error具体如下
ok,我们成功的对没有这个id的时候,返回了一些有用的信息了.但主业务逻辑看起来有点奇怪了,
不知道你注意到了没有,为了能够返回这个信息,我们把函数的返回改成了ResponseEntity
像上面这样就挺好的,不过这代码是有代价的,我们引入了一个新的玩意throw new SpittleNotFoundException(id);抛了一个异常出来,难道我们要玩自杀?居然抛异常出来?想让服务器奔溃?显然不可以,得找人来处理这个异常,然后返回错误信息才对啊,就在这关键的时候
@ExceptionHandler登场
他可以捕获我们跑出来的这个SpittleNotFoundException异常.
所以我们的Controller可以改成这样
注意
我们把spittleNotFound前面的@ResponseBody也去掉了.
下面是exception的具体内容,需要注意的是,他继承了RuntimeException
好了,这是一个异常的处理,但有个问题需要提出的是,这个抛出的异常,只在这个Controller中被处理.很显然在实际的开发中,还是有可能别的Controller中,抛出了一个一样的异常,所以我们需要一个能够统一处理这些异常的类,spring提供了下面的解决方案
@ControllerAdvice登场
ControllerAdvice的作用就是这样,全局的去捕获异常.
这样我们就可以把所有的处理都写在一个类里面了.感觉还是挺好的.
做完前面的工作铺垫,要来让@valid登场了
@Valid登场
我们的经常要求一些字段有大小,长度的的限制等,例如要求用户的密码不低于六位,名字不能为空,也不能多于20个字符等的限制.所以我们需要加多一个@Valid在我们要检验的类前面,来告诉spring,如果这家伙来了,我们要检查他.因为他可能不符合要求.检查出错的结果会在Errors里面,- 需要注意的是,Errors这个要紧跟在声明了@valid的object后面! -
检测的条件,我们需要用到@NotNull和@Size这两个来帮忙.
所有上面用到的msg在我们\resource文件夹新建的ValidationNameMessages.properties 里面,具体内容如下.
通过使用@Valid,我们简化了很多检验的操作.最原始的做法如下’
2.创建专门用来处理全局异常的类. 使用注解@ControllerAdvice
3.在该异常处理类中,使用@ExceptionHandler注解来标记该方法所处理的异常
4.使用@responseStatus注解来标记该异常处理方法返回的状态码和异常信息.
spring从4.0开始引入了@Restcontroller,这是对REST的支持,他可以帮我们去掉@ResponseBody这个
所以原本这样的代码,
@Controller public class SpitteController{ @RequestMapping(value="/{id}", method=RequestMethod.GET) public @ResponseBody Spittle spittleById(@PathVariable long id) { Spittle spittle = spittleRepository.findOne(id); if (spittle == null) { throw new SpittleNotFoundException(id); } return spittle; } }
使用@RestController后
@RestController public class SpitteController{ @RequestMapping(value="/{id}", method=RequestMethod.GET) public Spittle spittleById(@PathVariable long id) { Spittle spittle = spittleRepository.findOne(id); if (spittle == null) { throw new SpittleNotFoundException(id); } return spittle; } }
是的,少了一个@ResponseBody这个玩意.感觉也是挺舒服的,如果你这个Controller有几十个方法的话.
关于异常的处理
@RequestMapping(value="/{id}", method=RequestMethod.GET) public @ResponseBody Spittle spittleById(@PathVariable long id) { return spittleRepository.findOne(id); }
上面的代码看起来没什么问题.但如果没有这个id呢?返回的内容居然就是个null,那显然不太好.
所以我们需要加点返回信息,最少设置头部的状态为NO_FOUND像下面这样,
@RequestMapping(value="/{id}", method=RequestMethod.GET) public ResponseEntity<Spittle> spittleById(@PathVariable long id) { Spittle spittle = spittleRepository.findOne(id); HttpStatus status = spittle != null ? HttpStatus.OK : HttpStatus.NOT_FOUND; return new ResponseEntity<Spittle>(spittle, status); }
如果返回为空,我们返回的状态为HttpStatus.NOT_FOUND,虽然spittle这个为空依然没解决,也没返回什么具体的信息.所以我们该加点内容,例如msg为改ID无效之类的.
@RequestMapping(value="/{id}", method=RequestMethod.GET) public ResponseEntity<?> spittleById(@PathVariable long id) { Spittle spittle = spittleRepository.findOne(id); if (spittle == null) { Error error = new Error(Error.NOT_FOUND, "Spittle [" + id + "] not found"); return new ResponseEntity<Error>(error, HttpStatus.NOT_FOUND); } return new ResponseEntity<Spittle>(spittle, HttpStatus.OK); }
Error具体如下
public class Error { private int code; private String message; public Error(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } }
ok,我们成功的对没有这个id的时候,返回了一些有用的信息了.但主业务逻辑看起来有点奇怪了,
不知道你注意到了没有,为了能够返回这个信息,我们把函数的返回改成了ResponseEntity
@RequestMapping(value="/{id}", method=RequestMethod.GET) public Spittle spittleById(@PathVariable long id) { Spittle spittle = spittleRepository.findOne(id); if (spittle == null) { throw new SpittleNotFoundException(id); } return spittle; }
像上面这样就挺好的,不过这代码是有代价的,我们引入了一个新的玩意throw new SpittleNotFoundException(id);抛了一个异常出来,难道我们要玩自杀?居然抛异常出来?想让服务器奔溃?显然不可以,得找人来处理这个异常,然后返回错误信息才对啊,就在这关键的时候
@ExceptionHandler登场
@ExceptionHandler(SpittleNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public @ResponseBody Error spittleNotFound(SpittleNotFoundException e) { long spittleId = e.getSpittleId(); return new Error(4, "Spittle [" + spittleId + "] not found"); }
他可以捕获我们跑出来的这个SpittleNotFoundException异常.
所以我们的Controller可以改成这样
@RestController public class SpitteController{ @RequestMapping(value="/{id}", method=RequestMethod.GET) public Spittle spittleById(@PathVariable long id) { Spittle spittle = spittleRepository.findOne(id); if (spittle == null) { throw new SpittleNotFoundException(id); } return spittle; } @ExceptionHandler(SpittleNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public Error spittleNotFound(SpittleNotFoundException e) { long spittleId = e.getSpittleId(); return new Error(4, "Spittle [" + spittleId + "] not found"); } }
注意
我们把spittleNotFound前面的@ResponseBody也去掉了.
下面是exception的具体内容,需要注意的是,他继承了RuntimeException
public class SpittleNotFoundException extends RuntimeException { private long spittleId; public SpittleNotFoundException(long spittleId) { this.spittleId = spittleId; } public long getSpittleId() { return spittleId; } }
好了,这是一个异常的处理,但有个问题需要提出的是,这个抛出的异常,只在这个Controller中被处理.很显然在实际的开发中,还是有可能别的Controller中,抛出了一个一样的异常,所以我们需要一个能够统一处理这些异常的类,spring提供了下面的解决方案
@ControllerAdvice登场
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class AppWideExceptionHandler { @ExceptionHandler(SpittleNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public Error spittleNotFound(SpittleNotFoundException e) { long spittleId = e.getSpittleId(); return new Error(4, "Spittle [" + spittleId + "] not found"); } @ExceptionHandler(DuplicateSpittleException.class) public String duplicateSpittleHandler() { return "error/duplicate"; } }
ControllerAdvice的作用就是这样,全局的去捕获异常.
这样我们就可以把所有的处理都写在一个类里面了.感觉还是挺好的.
做完前面的工作铺垫,要来让@valid登场了
@Valid登场
@RequestMapping(value="/register", method=POST) public String processRegistration(@Valid Spitter spitter,Errors errors) { if (errors.hasErrors()) { throw new HasErrorsException(); } ... //一些业务操作. }
我们的经常要求一些字段有大小,长度的的限制等,例如要求用户的密码不低于六位,名字不能为空,也不能多于20个字符等的限制.所以我们需要加多一个@Valid在我们要检验的类前面,来告诉spring,如果这家伙来了,我们要检查他.因为他可能不符合要求.检查出错的结果会在Errors里面,- 需要注意的是,Errors这个要紧跟在声明了@valid的object后面! -
检测的条件,我们需要用到@NotNull和@Size这两个来帮忙.
public class User{ @NotNull @Size(min=5, max=16, message="{username.size}") private String username; @NotNull @Size(min=5, max=25, message="{password.size}") private String password; @NotNull @Size(min=2, max=30, message="{firstName.size}") private String firstName; @NotNull @Size(min=2, max=30, message="{lastName.size}") private String lastName; @NotNull @Email(message="{email.valid}") private String email; }
所有上面用到的msg在我们\resource文件夹新建的ValidationNameMessages.properties 里面,具体内容如下.
firstName.size= First name must be between {min} and {max} characters long. lastName.size= Last name must be between {min} and {max} characters long. username.size= Username must be between {min} and {max} characters long. password.size= Password must be between {min} and {max} characters long. email.valid=The email address must be valid.
通过使用@Valid,我们简化了很多检验的操作.最原始的做法如下’
@RequestMapping("/signUp") @ResponseBody public Result saveAccount(HttpServletRequest request,HttpServletResponse response) throws Exception{ String password =request.getParameter("password"); String name =request.getParameter("name"); String zone = request.getParameter("zone"); String code = request.getParameter("code"); String device =request.getParameter("device"); Result result = accountService.checkcode(phone, zone, code, password, device); if(password ==null || password.size<6||password.size>20) { result = "{" 错误信息"}"; response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); Writer writer = response.getWriter(); writer.write(result); writer.close(); return false; } } if(name ==null || name.size<6 || name.size>20) { ................ } .........一堆别的的检验 ........ }
总结
1.创建相对应的异常类.2.创建专门用来处理全局异常的类. 使用注解@ControllerAdvice
3.在该异常处理类中,使用@ExceptionHandler注解来标记该方法所处理的异常
4.使用@responseStatus注解来标记该异常处理方法返回的状态码和异常信息.
相关文章推荐
- 使用@RestController,@ExceptionHandler和@Valid,把检验和异常处理从主要业务逻辑里面抽离出来
- Spring中的@ControllerAdvice注解配合@ExceptionHandler使用实现异常处理
- 使用@ControllerAdvice及@ExceptionHandler(value = Exception.class)全局异常处理
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- @ControllerAdvice + @ExceptionHandler 全局处理 Controller 层异常
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- Android中使用UncaughtExceptionHandler来处理未捕获的异常
- Spring中@ControllerAdvice注解配合@ExceptionHandler实现统一异常处理
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- 使用Spring MVC HandlerExceptionResolver处理异常
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- 使用Spring中的ExceptionHandlerExceptionResolver进行统一的异常处理
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- Spring MVC的Controller统一异常处理:HandlerExceptionResolver
- Java异常处理之处理未捕获的异常及UncaughtExceptionHandler的使用
- spring mvc的controller统一异常处理handlerExceptionResolver
- 使用Spring MVC HandlerExceptionResolver处理异常