1, Examples of abnormal handling
Chaos 1: only output to the console after capturing exceptions
Front end JS Ajax code
$.ajax({ type: "GET", url: "/user/add", dataType: "json", success: function(data){ alert("Added successfully"); } });
Back end business code
try { // do something } catch (XyyyyException e) { e.printStackTrace(); }
Question:
- The back end directly captures exceptions and only prints logs. The user experience is very poor. Once there is an error in the background, the user has no perception and the page is stateless,.
- If no one pays regular attention to the log, no one will find any exceptions in the system
Chaos II: chaotic return mode
Front end code
$.ajax({ type: "GET", url: "/goods/add", dataType: "json", success: function(data) { if (data.flag) { alert("Added successfully"); } else { alert(data.message); } }, error: function(data){ alert("Add failed"); } });
Back end code
@RequestMapping("/goods/add") @ResponseBody public Map add(Goods goods) { Map map = new HashMap(); try { // do something map.put(flag, true); } catch (Exception e) { e.printStackTrace(); map.put("flag", false); map.put("message", e.getMessage()); } reutrn map; }
Question:
- The data returned by each person has its own specification. Your name is flag and his name is isOK. Your success code is 0 and its success code is 0000. This leads to a large number of exception return logic codes written on the back end, and a set of exception handling logic for each request on the front end. A lot of duplicate code.
- If the front-end and back-end are developed by one person, they can barely be used. If the front-end and back-end are separated, it is a system disaster.
Reference: juejin.im/post/5c3ea9
2, How to design exception handling
Friendly to interested parties
- Back end developers have a single responsibility. They only need to capture and convert exceptions into custom exceptions and throw them all the time. There is no need to think about the design of page Jump 404 and the data structure of abnormal response.
- It is friendly to the front-end personnel. The data returned by the back-end to the front-end should have a unified data structure and unified specifications. A data structure that cannot respond to one person. In this process, there is no need for back-end developers to do more work and give it to the global exception handler to handle the conversion from "exception" to "response data structure".
- User friendly, users can clearly know the cause of exceptions. This requires custom exceptions, global unified processing, ajax interface request response, unified exception data structure, and page template request to jump to 404 page.
- It is operation and maintenance friendly, and the abnormal information is reasonably and regularly persisted for query.
Why do you want to convert system runtime exception capture to custom exception throw? A: because the user doesn't know ConnectionTimeOutException is something like this exception, but converting to a custom exception requires the programmer to translate the runtime exception. For example, there should be a message field in the custom exception, and the back-end programmer should explicitly use a user-oriented friendly language in the message field to explain what happened.
3, Development specification
- Exceptions intercepted by Controller, Service and DAO layers are converted into custom exceptions, and exceptions are not allowed to be intercepted privately. Must be thrown out.
- Unified data response code, use httpstatusode, do not customize. Custom inconvenient memory. 200 successful request, 400 exceptions caused by user input errors, 500 system internal exceptions, 999 unknown exceptions.
- There is a message attribute in the custom exception. The exception must be described in a friendly language and assigned to message
- It is not allowed to uniformly catch the parent class exception. It should be divided into subclasses of catch, so that exceptions can be clearly converted into user-defined exceptions and passed to the front end.
4, Page class exception handling
When we make a page template, the Controller has an exception. What should we do? You should jump to page 404.
Problem: the programmer throws a custom exception. After the global exception is intercepted, it returns @ ResponseBody AjaxResponse, not ModelAndView, so we can't jump to the error.html page. How should we handle the global exception of the page? Answer:
- Convert CustomException to ModelAndViewException in an aspect oriented manner.
- The global exception handler intercepts the ModelAndViewException and returns ModelAndView, that is, the error.html page
- The pointcut is the Controller layer method annotated with @ ModelView
Using this method to handle page class exceptions, programmers only need to add @ ModelView annotation to the Controller of page Jump
Wrong writing
@GetMapping("/freemarker") public String index(Model model) { try{ List<ArticleVO> articles = articleRestService.getAll(); model.addAttribute("articles", articles); }catch (Exception e){ return "error"; } return "fremarkertemp"; }
Correct writing
@ModelView @GetMapping("/freemarker") public String index(Model model) { List<ArticleVO> articles = articleRestService.getAll(); model.addAttribute("articles", articles); return "fremarkertemp"; }
5, Dealing with page global exceptions with aspect oriented method
Because aspect oriented programming is used, maven dependency package is introduced
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
Define a new exception class ModelViewException
public class ModelViewException extends RuntimeException{ //Exception error code private int code ; //Abnormal information private String message; public static ModelViewException transfer(CustomException e) { return new ModelViewException(e.getCode(),e.getMessage()); } private ModelViewException(int code, String message){ this.code = code; this.message = message; } int getCode() { return code; } @Override public String getMessage() { return message; } }
ModelView annotation is only used for annotation
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD})//This annotation can only be used on methods public @interface ModelView { }
Take the @ ModelView annotation as the starting point, face to face programming, convert CustomException into ModelViewException and throw it.
@Aspect @Component @Slf4j public class ModelViewAspect { //Set pointcut: methods annotated by @ ModelView are directly intercepted here @Pointcut("@annotation(club.krislin.exception.ModelView)") public void pointcut() { } /** * When an exception is thrown by a method annotated with ModelView, do the following */ @AfterThrowing(pointcut="pointcut()",throwing="e") public void afterThrowable(Throwable e) { log.error("Exception occurred in section:", e); if(e instanceof CustomException){ throw ModelViewException.transfer((CustomException) e); } } }
Global exception handler:
@ExceptionHandler(ModelViewException.class) public ModelAndView viewExceptionHandler(HttpServletRequest req, ModelViewException e) { ModelAndView modelAndView = new ModelAndView(); //Set exception information, such as modelAndView modelAndView.addObject("exception", e); modelAndView.addObject("url", req.getRequestURL()); modelAndView.setViewName("error"); //Return ModelAndView return modelAndView; }