概念
異常,在程序中經常發生,如果發生異常怎樣給用戶一個良好的反饋體驗就是我們需要處理的問題。以前處理異常信息,經常都是給前端一個統一的響應,如數據錯誤,程序崩潰等等。沒辦法指出哪里出錯了,這是一種對用戶很不友好的體驗。我們應該根據自己的業務給予信息提示
異常類
定義一個全局的異常類,有異常信息,都交到這邊來。它像一個污水處理廠,匯集所有的工業污水,然后分門別類進行污水凈化。要現實這種功能就要用到springBoot的@ControllerAdvice注解,它的作用是控制器增加,應用到有以下的注解的函數或類@ExceptionHandler,@InitBinder,@ModelAttribute。
創建一個異常類
package com.xmlxy.exception; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class CommonExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public Map<String,Object> exceptionHandler(Exception e) { Map<String,Object> result = new HashMap<String,Object>(); result.put("code","9999"); result.put("msg","異常被捕獲了"); return result; } }
寫個接口測試一下
@RequestMapping(value = "test",method = RequestMethod.GET)
public String test() { int i = 1 / 0; return "test接口"; }
我們都知道這會讓控制台拋出一個除數不能為零的異常信息。調用接口,會發現控制台並沒有異常信息,頁面輸出
{"msg":"除數不能為零","code":"9999"}
然后前端根據我們約定的code,或彈出窗,或跳轉頁面。但是這樣是遠遠不夠的,因為異常信息很多,我上文中,因為貪圖省事,直接用Exception捕獲並沒有給予詳細的異常捕捉。如果多余的不同異常,需要進行不同的異常處理,就可以編寫多個exceptionHandler方法,可以指定處理異常類。
@ExceptionHandler(FileNotFoundException.class) @ResponseBody public Map<String,Object> exceptionHandler(FileNotFoundException e) { Map<String,Object> result = new HashMap<String, Object>(); result.put("code","8888"); result.put("msg","文件不存在"); return result; }
自定義異常
現有的異常有時並沒有滿足我們業務需求,就得自定義自己的專屬異常類,舉一個前幾次講的登錄接口demo,用戶可能沒輸密碼直接點登錄,是進行密碼錯誤提示,還是反饋密碼為空哪種體驗比較好,很明顯是后一種。
自定義異常,繼承Exception接口
package com.xmlxy.exception; public class FistSpringBootException extends Exception { private String code; private FistSpringBootException(String code) { super(); this.code = code; } public FistSpringBootException(String code,String message) { super(message); this.code = code; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } }
封裝一個數據效驗類
package com.xmlxy.exception; import org.springframework.util.StringUtils; public class ParamUtil { /*驗證是否是數字*/ public static int isNumber(String args,String name) throws FistSpringBootException { if (StringUtils.isEmpty(args) && !name.matches("^[0-9]*$")) { throw new FistSpringBootException("1111",name + "參數不合法"); } return Integer.parseInt(args); } /*驗證參數是否為空*/ public static String isEmpty(String args,String name) throws FistSpringBootException { if (StringUtils.isEmpty(args)) { throw new FistSpringBootException("2222",name + "參數不能為空"); } return String.valueOf(args); } }
登錄接口
@RestController
public class LoginController { @RequestMapping(value = "login",method = RequestMethod.GET) public String login(HttpServletRequest request) throws FistSpringBootException { String user = ParamUtil.isEmpty(request.getParameter("user"),"user"); String pwd = ParamUtil.isEmpty(request.getParameter("pwd"),"pwd"); HttpSession session = request.getSession(); if ("admin".equals(user) && "admin".equals(pwd)) { User user1 = new User(); user1.setUser(user); user1.setPwd(pwd); session.setAttribute("user",user1); return "登錄成功"; } return "密碼錯誤,登錄失敗"; } }
進行異常捕獲
package com.xmlxy.exception; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import java.io.FileNotFoundException; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class CommonExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public Map<String,Object> exceptionHandler(Exception e) { Map<String,Object> result = new HashMap<String,Object>(); result.put("code","9999"); result.put("msg","除數不能為零"); return result; } @ExceptionHandler(FileNotFoundException.class) @ResponseBody public Map<String,Object> exceptionHandler(FileNotFoundException e) { Map<String,Object> result = new HashMap<String, Object>(); result.put("code","8888"); result.put("msg","文件不存在"); return result; } @ExceptionHandler(FistSpringBootException.class) @ResponseBody public Map<String,Object> exceptionHandler(FistSpringBootException e) { Map<String,Object> result = new HashMap<String,Object>(); result.put("code","1111"); result.put("msg",e.getMessage()); return result; } }
demo寫完,直接測試一下,訪問 http://127.0.0.1:8080/login?user=&pwd=

是否會發現自己還得效驗數據有點費勁,沒事,springBoot已經幫我們想好了,很簡單,只要加上注解。
User類
package com.xmlxy.bean; import lombok.Data; import org.springframework.stereotype.Component; import javax.validation.constraints.NotBlank; @Component @Data public class User { @NotBlank(message = "user不能為空") private String user; @NotBlank(message = "pwd不能為空") private String pwd; }
異常全局配置
@ExceptionHandler(BindException.class) @ResponseBody public Map<String,Object> exceptionHandler(BindException e) { Map<String,Object> result = new HashMap<String,Object>(); result.put("code","1111"); result.put("msg","參數不合法"); return result; }
登錄接口
@RestController
public class LoginController { @RequestMapping(value = "login",method = RequestMethod.GET) public String login(@Valid User user, HttpServletRequest request) { try { HttpSession session = request.getSession(); if ("admin".equals(user.getUser()) && "admin".equals(user.getPwd())) { session.setAttribute("user",user); return "登錄成功"; } }catch (Exception e){ e.printStackTrace(); } return "密碼錯誤,登錄失敗"; } }
再次訪問,會發現已經幫你效驗了
{"msg":"參數不合法","code":"1111"}
還有很多的數據效驗注解,比如@Email注解,接收參數必須是email地址,還有限制長度的@Length注解,以下是常用的校驗注解
@Null 被注釋的元素必須為 null @NotNull 被注釋的元素必須不為 null @AssertTrue 被注釋的元素必須為 true @AssertFalse 被注釋的元素必須為 false @Min(value) 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值 @Max(value) 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值 @DecimalMin(value) 被注釋的元素必須是一個數字,其值必須大於等於指定的最小值 @DecimalMax(value) 被注釋的元素必須是一個數字,其值必須小於等於指定的最大值 @Size(max=, min=) 被注釋的元素的大小必須在指定的范圍內 @Digits (integer, fraction) 被注釋的元素必須是一個數字,其值必須在可接受的范圍內 @Past 被注釋的元素必須是一個過去的日期 @Future 被注釋的元素必須是一個將來的日期 @Pattern(regex=,flag=) 被注釋的元素必須符合指定的正則表達式 Hibernate Validator提供的校驗注解: @NotBlank(message =) 驗證字符串非null,且長度必須大於0 @Email 被注釋的元素必須是電子郵箱地址 @Length(min=,max=) 被注釋的字符串的大小必須在指定的范圍內 @NotEmpty 被注釋的字符串的必須非空 @Range(min=,max=,message=) 被注釋的元素必須在合適的范圍內
