springBoot資料校驗與統一異常處理

農碼關山北發表於2019-07-31

概念

  異常,在程式中經常發生,如果發生異常怎樣給使用者一個良好的反饋體驗就是我們需要處理的問題。以前處理異常資訊,經常都是給前端一個統一的響應,如資料錯誤,程式崩潰等等。沒辦法指出哪裡出錯了,這是一種對使用者很不友好的體驗。我們應該根據自己的業務給予資訊提示

異常類

  定義一個全域性的異常類,有異常資訊,都交到這邊來。它像一個汙水處理廠,彙集所有的工業汙水,然後分門別類進行汙水淨化。要現實這種功能就要用到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=)  被註釋的元素必須在合適的範圍內

 

相關文章