中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

SpringBoot | 第八章:統(tǒng)一異常、數(shù)據(jù)校驗(yàn)處理

2018-08-06    來(lái)源:importnew

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用

前言

在web應(yīng)用中,請(qǐng)求處理時(shí),出現(xiàn)異常是非常常見的。所以當(dāng)應(yīng)用出現(xiàn)各類異常時(shí),進(jìn)行異常的捕獲或者二次處理(比如sql異常正常是不能外拋)是非常必要的,比如在開發(fā)對(duì)外api服務(wù)時(shí),約定了響應(yīng)的參數(shù)格式,如respCode、respMsg,調(diào)用方根據(jù)錯(cuò)誤碼進(jìn)行自己的業(yè)務(wù)邏輯。本章節(jié)就重點(diǎn)講解下統(tǒng)一異常和數(shù)據(jù)校驗(yàn)處理。

springboot中,默認(rèn)在發(fā)送異常時(shí),會(huì)跳轉(zhuǎn)值/error請(qǐng)求進(jìn)行錯(cuò)誤的展現(xiàn),根據(jù)不同的Content-Type展現(xiàn)不同的錯(cuò)誤結(jié)果,如json請(qǐng)求時(shí),直接返回json格式參數(shù)。

瀏覽器訪問(wèn)異常時(shí):

使用postman訪問(wèn)時(shí):

統(tǒng)一異常處理

顯然,默認(rèn)的異常頁(yè)是對(duì)用戶或者調(diào)用者而言都是不友好的,所以一般上我們都會(huì)進(jìn)行實(shí)現(xiàn)自己業(yè)務(wù)的異常提示信息。

創(chuàng)建全局的統(tǒng)一異常處理類

利用@ControllerAdvice@ExceptionHandler定義一個(gè)統(tǒng)一異常處理類

  • @ControllerAdvice:控制器增強(qiáng),使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法應(yīng)用到所有的 @RequestMapping注解的方法。
  • @ExceptionHandler:異常處理器,此注解的作用是當(dāng)出現(xiàn)其定義的異常時(shí)進(jìn)行處理的方法

創(chuàng)建異常類:CommonExceptionHandler

@ControllerAdvice
public class CommonExceptionHandler {

    /**
     *  攔截Exception類的異常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Map<String,Object> exceptionHandler(Exception e){
        Map<String,Object> result = new HashMap<String,Object>();
        result.put("respCode", "9999");
        result.put("respMsg", e.getMessage());
        //正常開發(fā)中,可創(chuàng)建一個(gè)統(tǒng)一響應(yīng)實(shí)體,如CommonResp
        return result; 
    }
}

多余不同異常(如自定義異常),需要進(jìn)行不同的異常處理時(shí),可編寫多個(gè)exceptionHandler方法,注解ExceptionHandler指定處理的異常類,如

/**
 * 攔截 CommonException 的異常
 * @param ex
 * @return
 */
@ExceptionHandler(CommonException.class)
@ResponseBody
public Map<String,Object> exceptionHandler(CommonException ex){
    log.info("CommonException:{}({})",ex.getMsg(), ex.getCode());
    Map<String,Object> result = new HashMap<String,Object>();
    result.put("respCode", ex.getCode());
    result.put("respMsg", ex.getMsg());
    return result; 
}

由于加入了@ResponseBody,所以返回的是json格式,

說(shuō)明異常已經(jīng)被攔截了。

可攔截不同的異常,進(jìn)行不同的異常提示,比如NoHandlerFoundExceptionHttpMediaTypeNotSupportedException、AsyncRequestTimeoutException等等,這里就不列舉了,讀者可自己加入后實(shí)際操作下。

對(duì)于返回頁(yè)面時(shí),返回ModelAndView即可,如

@ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }

由于工作中都是才有前后端分離開發(fā)模式,所以一般上都沒(méi)有直接返回資源頁(yè)的需求了,一般上都是返回固定的響應(yīng)格式,如respCode、respMsg、data,前端通過(guò)判斷respCode的值進(jìn)行業(yè)務(wù)判斷,是彈窗還是跳轉(zhuǎn)頁(yè)面。

數(shù)據(jù)校驗(yàn)

在web開發(fā)時(shí),對(duì)于請(qǐng)求參數(shù),一般上都需要進(jìn)行參數(shù)合法性校驗(yàn)的,原先的寫法時(shí)一個(gè)個(gè)字段一個(gè)個(gè)去判斷,這種方式太不通用了,所以java的JSR 303: Bean Validation規(guī)范就是解決這個(gè)問(wèn)題的。

JSR 303只是個(gè)規(guī)范,并沒(méi)有具體的實(shí)現(xiàn),目前通常都是才有hibernate-validator進(jìn)行統(tǒng)一參數(shù)校驗(yàn)。

JSR303定義的校驗(yàn)類型

Constraint 詳細(xì)信息
@Null 被注釋的元素必須為?null
@NotNull 被注釋的元素必須不為?null
@AssertTrue 被注釋的元素必須為?true
@AssertFalse 被注釋的元素必須為?false
@Min(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@Max(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
@DecimalMin(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值
@DecimalMax(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值
@Size(max, min) 被注釋的元素的大小必須在指定的范圍內(nèi)
@Digits (integer, fraction) 被注釋的元素必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi)
@Past 被注釋的元素必須是一個(gè)過(guò)去的日期
@Future 被注釋的元素必須是一個(gè)將來(lái)的日期
@Pattern(value) 被注釋的元素必須符合指定的正則表達(dá)式

Hibernate Validator 附加的 constraint

Constraint 詳細(xì)信息
@Email 被注釋的元素必須是電子郵箱地址
@Length 被注釋的字符串的大小必須在指定的范圍內(nèi)
@NotEmpty 被注釋的字符串的必須非空
@Range 被注釋的元素必須在合適的范圍內(nèi)

創(chuàng)建實(shí)體類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DemoReq {
    
    @NotBlank(message="code不能為空")
    String code;
    
    @Length(max=10,message="name長(zhǎng)度不能超過(guò)10")
    String name;

}

然后在控制層方法里,加入@Valid即可,這樣在訪問(wèn)前,會(huì)對(duì)請(qǐng)求參數(shù)進(jìn)行檢驗(yàn)。

@GetMapping("/demo/valid")
public String demoValid(@Valid DemoReq req) {
    return req.getCode() + "," + req.getName();
}

啟動(dòng),后訪問(wèn)http://127.0.0.1:8080/demo/valid

加上正確參數(shù)后,http://127.0.0.1:8080/demo/valid?code=3&name=s

這樣數(shù)據(jù)的統(tǒng)一校驗(yàn)就完成了,對(duì)于其他注解的使用,大家可自行谷歌下,基本上都很簡(jiǎn)單的,對(duì)于已有的注解無(wú)法滿足校驗(yàn)需要時(shí),也可進(jìn)行自定義注解的開發(fā),一下簡(jiǎn)單講解下,自定義注解的編寫

不使用@valid的情況下,也可利用編程的方式編寫一個(gè)工具類,進(jìn)行實(shí)體參數(shù)校驗(yàn)

public class ValidatorUtil {
    private static Validator validator = ((HibernateValidatorConfiguration) Validation
            .byProvider(HibernateValidator.class).configure()).failFast(true).buildValidatorFactory().getValidator();

    /**
     * 實(shí)體校驗(yàn)
     * 
     * @param obj
     * @throws CommonException
     */
    public static <T> void validate(T obj) throws CommonException {
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(obj, new Class[0]);
        if (constraintViolations.size() > 0) {
            ConstraintViolation<T> validateInfo = (ConstraintViolation<T>) constraintViolations.iterator().next();
            // validateInfo.getMessage() 校驗(yàn)不通過(guò)時(shí)的信息,即message對(duì)應(yīng)的值
            throw new CommonException("0001", validateInfo.getMessage());
        }
    }
}

使用

@GetMapping("/demo/valid")
public String demoValid(@Valid DemoReq req) {
    //手動(dòng)校驗(yàn)
    ValidatorUtil.validate(req);
    return req.getCode() + "," + req.getName();
}

自定義校驗(yàn)注解

自定義注解,主要時(shí)實(shí)現(xiàn)ConstraintValidator的處理類即可,這里已編寫一個(gè)校驗(yàn)常量的注解為例:參數(shù)值只能為特定的值。

自定義注解

@Documented
//指定注解的處理類
@Constraint(validatedBy = {ConstantValidatorHandler.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Constant {

   String message() default "{constraint.default.const.message}";

   Class<?>[] groups() default {};

   Class<? extends Payload>[] payload() default {};

   String value();

}

注解處理類

public class ConstantValidatorHandler implements ConstraintValidator<Constant, String> {

    private String constant;

    @Override
    public void initialize(Constant constraintAnnotation) {
        //獲取設(shè)置的字段值
        this.constant = constraintAnnotation.value();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        //判斷參數(shù)是否等于設(shè)置的字段值,返回結(jié)果
        return constant.equals(value);
    }

}

使用

@Constant(message = "verson只能為1.0",value="1.0")
String version;

運(yùn)行:

此時(shí),自定義注解已生效。大家可根據(jù)實(shí)際需求進(jìn)行開發(fā)。

大家看到在校驗(yàn)不通過(guò)時(shí),返回的異常信息是不友好的,此時(shí)可利用統(tǒng)一異常處理,對(duì)校驗(yàn)異常進(jìn)行特殊處理,特別說(shuō)明下,對(duì)于異常處理類,共有以下幾種情況(被@RequestBody和@RequestParam注解的請(qǐng)求實(shí)體,校驗(yàn)異常類是不同的)

@ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String,Object> handleBindException(MethodArgumentNotValidException ex) {
        FieldError fieldError = ex.getBindingResult().getFieldError();
        log.info("參數(shù)校驗(yàn)異常:{}({})", fieldError.getDefaultMessage(),fieldError.getField());
        Map<String,Object> result = new HashMap<String,Object>();
        result.put("respCode", "01002");
        result.put("respMsg", fieldError.getDefaultMessage());
        return result;
    }


    @ExceptionHandler(BindException.class)
    public Map<String,Object> handleBindException(BindException ex) {
        //校驗(yàn) 除了 requestbody 注解方式的參數(shù)校驗(yàn) 對(duì)應(yīng)的 bindingresult 為 BeanPropertyBindingResult
        FieldError fieldError = ex.getBindingResult().getFieldError();
        log.info("必填校驗(yàn)異常:{}({})", fieldError.getDefaultMessage(),fieldError.getField());
        Map<String,Object> result = new HashMap<String,Object>();
        result.put("respCode", "01002");
        result.put("respMsg", fieldError.getDefaultMessage());
        return result;
    }

啟動(dòng)后,提示就友好了

所以統(tǒng)一異常還是很有必要的。

總結(jié)

本章節(jié)主要是闡述了統(tǒng)一異常處理和數(shù)據(jù)的合法性校驗(yàn),同時(shí)簡(jiǎn)單實(shí)現(xiàn)了一個(gè)自定義的注解類,大家在碰見已有注解無(wú)法解決時(shí),可通過(guò)自定義的形式進(jìn)行,當(dāng)然對(duì)于通用而已,利用@Pattern(正則表達(dá)式)基本上都是可以實(shí)現(xiàn)的。

最后

目前互聯(lián)網(wǎng)上很多大佬都有springboot系列教程,如有雷同,請(qǐng)多多包涵了。本文是作者在電腦前一字一句敲的,每一步都是實(shí)踐的。若文中有所錯(cuò)誤之處,還望提出,謝謝。

標(biāo)簽: 谷歌 互聯(lián)網(wǎng)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:MySQL并行復(fù)制的深入淺出

下一篇:SpringBoot | 第七章:過(guò)濾器、監(jiān)聽器、攔截器