这是有关使用Spring进行REST异常处理的系列的第二篇文章。 在我以前的文章中,我描述了如何在REST服务中组织最简单的异常处理。 这次,我将更进一步,我将向您展示何时最好在@ControllerAdvice级别上使用异常处理 。
介绍
在开始本文的技术部分之前,我需要考虑一种情况,那就是我们最好在@ControllerAdvice级别上使用异常处理。
通常,一个控制器负责与一种类型的实体相关的整个逻辑。 就是说,如果我有一些EntityController类,它将包含与实体有关的所有CRUD(创建,读取,更新,删除)操作,如果需要的话,还可能包含一些额外的逻辑。 让我们检查三个操作:读取,更新,删除。
读取操作会根据ID(我们将其作为参数传递给它)返回一些特定的实体。 如果实体不存在,则读取操作将返回null。 更新/删除操作分别更新/删除特定实体。 这两个操作中的每一个都包含读取操作,因为在更新/删除实体之前,我们需要确保其存在于系统中。
在更新/删除操作过程中未找到实体时,应用程序将抛出EntityNotFoundException异常。 在这种情况下,异常处理将非常简单。 该应用程序必须将信息返回给客户端:
- 响应标题:404
- 导致异常的链接
- 错误消息:没有ID为N的实体
对于此类异常,这是最简单的响应结构。 因此,无论您在一个应用程序中有多少个不同的实体类,因为您可以用相同的方式处理类似类型的异常(例如,没有此类实体)。 @ControllerAdvice批注使这成为可能。
@ControllerAdvice级别的异常处理
本文的实际部分将基于上一教程的申请表。
首先,我需要在message.properties文件中添加一条错误消息:
error.no.smartphone.id = There is no Smartphone with id:
之后,让我们看一下本文主题中对我们来说有趣的控制器方法。
...@RequestMapping(value="/edit/{id}", method=RequestMethod.GET)public ModelAndView editSmartphonePage(@PathVariable int id) {ModelAndView mav = new ModelAndView("phones/edit-phone");Smartphone smartphone = smartphoneService.get(id);mav.addObject("sPhone", smartphone);return mav;}@RequestMapping(value="/edit/{id}", method=RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)@ResponseBodypublic Smartphone editSmartphone(@PathVariable int id, @Valid @RequestBody Smartphone smartphone) {smartphone.setId(id);return smartphoneService.update(smartphone);}
...@RequestMapping(value="/delete/{id}", method=RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)@ResponseBodypublic Smartphone deleteSmartphone(@PathVariable int id) {return smartphoneService.delete(id);}
...
这些方法包括SmartphoneService的调用。 而且SmartphoneService的实现包含可以引发SmartphoneNotFoundException的方法 。
@Service
@Transactional(rollbackFor = { SmartphoneNotFoundException.class })
public class SmartphoneServiceImpl implements SmartphoneService {@Autowiredprivate SmartphoneRepository smartphoneRepository;@Overridepublic Smartphone create(Smartphone sp) {return smartphoneRepository.save(sp);}@Overridepublic Smartphone get(Integer id) {Smartphone sp = null;if (id instanceof Integer)sp = smartphoneRepository.findOne(id);if (sp != null)return sp;throw new SmartphoneNotFoundException(id);}@Overridepublic List getAll() {return smartphoneRepository.findAll();}@Overridepublic Smartphone update(Smartphone sp) {Smartphone sPhoneToUpdate = get(sp.getId());sPhoneToUpdate.update(sp);return sPhoneToUpdate;}@Overridepublic Smartphone delete(Integer id) {Smartphone sPhone = get(id);smartphoneRepository.delete(id);return sPhone;}}
这是SmartphoneNotFoundException的代码:
public class SmartphoneNotFoundException extends RuntimeException {private static final long serialVersionUID = -2859292084648724403L;private final int smartphoneId;public SmartphoneNotFoundException(int id) {smartphoneId = id;}public int getSmartphoneId() {return smartphoneId;}}
最后,我可以移至@ControllerAdvice 。
@ControllerAdvice
public class RestExceptionProcessor {@Autowiredprivate MessageSource messageSource;@ExceptionHandler(SmartphoneNotFoundException.class)@ResponseStatus(value=HttpStatus.NOT_FOUND)@ResponseBodypublic ErrorInfo smartphoneNotFound(HttpServletRequest req, SmartphoneNotFoundException ex) {Locale locale = LocaleContextHolder.getLocale();String errorMessage = messageSource.getMessage("error.no.smartphone.id", null, locale);errorMessage += ex.getSmartphoneId();String errorURL = req.getRequestURL().toString();return new ErrorInfo(errorURL, errorMessage);}}
异常处理程序方法返回ErrorInfo对象。 您可以在上一则有关@Controller级别的异常处理的文章中了解有关它的更多信息。
这样,我们只需将额外的异常类添加到@ExceptionHandler批注中,就可以在一个地方收集所有类似的异常。 这种方法使整个应用程序内的代码维护更加容易。
示例说明:
注意:我发出了id值为356的请求,但是数据库中没有任何记录与此ID值相对应。 这种情况导致异常。
翻译自: https://www.javacodegeeks.com/2013/11/spring-rest-exception-handling-vol-2.html