hibernate-validator校驗(yàn)

hibernate validator 校驗(yàn)操作小結(jié)

 @Override
    public Result<List<Month>> getCalendarList(@Validated({ calendar.class }) @RequestBody GroupDTO dto)}...

在controller 的方法定義中加上 @Validated({ calendar.class }) 注解
(calendar.class 為校驗(yàn)分組 ),那么按照該分組的規(guī)則校驗(yàn)前端傳過(guò)來(lái)的值是否滿足定義的校驗(yàn)規(guī)則。

同一個(gè)dto可能會(huì)在多個(gè)handler的方法中使用,但是可能會(huì)要按照不同的校驗(yàn)規(guī)則分組,如insert的controller需要這個(gè)對(duì)象的屬性都非空,但是刪除的controller只需要id非空即可。那么在dto定義的校驗(yàn)規(guī)則就需要分組:


    @NotNull(message="{ProductEmpty}",groups = {deleteGroup.class, addPriceList.class, addPriceWeeks.class,calendarPrice.class})
    private Integer productId;
    @NotNull(message="{MenuEmpty}",groups = {deleteGroup.class, addPriceList.class,addPriceWeeks.class,calendarPrice.class })
    private Integer menuId;

如上所示在校驗(yàn)注解中有g(shù)roups屬性可以定義這個(gè)校驗(yàn)是屬于哪個(gè)分組的,而屬性的值只是一個(gè)空的接口,在controller方法的參數(shù)前使用@Validated({ calendar.class }) 即指定了這個(gè)方法使用哪組校驗(yàn),那么就只會(huì)校驗(yàn)該dto下groups中有該組的屬性。

message中返回的是校驗(yàn)失敗的信息 可以直接寫(xiě)字符串如 “****不能為空” 也可以如上所示在定義一個(gè)properties文件統(tǒng)一每個(gè)返回信息。


返回信息定義

可以看到這個(gè)文件名字中有zh_CN,這個(gè)其實(shí)是jar包中自帶的中文返回信息配置,我們同樣可以在hibernate-validator的jar包下找到英文的配置文件 配置它 這樣方便國(guó)際化。

DateEmpty=\u65E5\u671F\u4FE1\u606F\u5FC5\u4F20
ProductEmpty=\u4EA7\u54C1\u4FE1\u606F\u4E0D\u5168

具體配置中是這樣的 配置信息被轉(zhuǎn)碼
配置的好處是有一個(gè)地方統(tǒng)一管理了返回信息,方便修改。

校驗(yàn)規(guī)則制定

1 普通校驗(yàn)

注解類(lèi)型

把該注解的屬性填好 加到被校驗(yàn)對(duì)象的字段上就可以了 注意一定要類(lèi)型 匹配 如@MIN加到string頭上 運(yùn)行就會(huì)報(bào)錯(cuò)

2 復(fù)合校驗(yàn)

如果說(shuō) 我的對(duì)象里有對(duì)象屬性 ,對(duì)象的屬性里又有對(duì)象 那么對(duì)這種深層次的校驗(yàn)應(yīng)該這么做:

 @Override
    public Result<List<Month>> getCalendarList(@Validated({ calendar.class }) @RequestBody GroupDTO dto
            ) {....

方法定義這里不用變

dto校驗(yàn)規(guī)則中

    @NotEmpty(message="價(jià)格信息必傳",groups = { addPriceList.class,addPriceWeeks.class })
    private List<@Valid PriceInfo> priceInfos;

首先在校驗(yàn)的屬性對(duì)象前加上@validate注解
然后定義這個(gè)對(duì)象的校驗(yàn)規(guī)則,即對(duì)priceInfo中各個(gè)屬性加上校驗(yàn)注解當(dāng)然想要在這里被校驗(yàn)的字段的校驗(yàn)分組要和外面一直為addPriceList.class 或另一個(gè)。

3 自定義校驗(yàn)

如果說(shuō)框架提供給我的校驗(yàn)不能滿足我的場(chǎng)景需求 那么我們可以自定義校驗(yàn)

屬性單獨(dú)校驗(yàn)

如果我們要定義一個(gè)對(duì)單獨(dú)屬性的校驗(yàn)類(lèi)型:

package admtic.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

import admtic.validator.IsNumValidator;

@Documented
@Constraint(validatedBy = IsNumValidator.class)
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNum {
    /*
     * 用于驗(yàn)證的注解下列這三個(gè)方法必須要,這是Hibernate Validation框架要求的,否則程序再在調(diào)用的時(shí)候會(huì)報(bào)錯(cuò)
     * default用于對(duì)屬性給定默認(rèn)值
     *  如果不給定默認(rèn)值,則在使用注解的時(shí)候必須給屬性指定屬性值,否則報(bào)錯(cuò)
     *  給定默認(rèn)值時(shí),在使用注解的時(shí)候可以不用指定屬性值
     */
    String message() default "不是數(shù)字!";

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

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

//    非必須屬性
//    // 沒(méi)加default給定默認(rèn)值,使用注解的時(shí)候該屬性必須賦值,否則報(bào)錯(cuò)
//    String regex();
//    // value屬性,加上了default "mercy" 使得該屬性在使用注解的時(shí)候可以不用輸入也不會(huì)報(bào)錯(cuò)
//    String value() default "mercy";
}

先自定義一個(gè)校驗(yàn)注解 然后再實(shí)現(xiàn)一個(gè)校驗(yàn)器

package admtic.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import admtic.annotation.IsNum;

/**
 * 泛型中一個(gè)是枚舉類(lèi)一個(gè)是校驗(yàn)支持的數(shù)據(jù)類(lèi)型
 * @author spf
 * @version 2019年5月8日 下午3:46:35 
 *
 */
public class IsNumValidator implements ConstraintValidator<IsNum, Integer> {
//    private String regex;
    /**
     * 通過(guò)initialize()可以獲取注解里的屬性值
     */
//    @Override
//    public void initialize(IsNum constraintAnnotation) {
//        ConstraintValidator.super.initialize(constraintAnnotation);
//        regex = constraintAnnotation.regex();
//    }

    /**
     * 強(qiáng)行return true hahahhah
     */
    @Override
    public boolean isValid(Integer s, ConstraintValidatorContext arg1) {
        if(s == -1){ 
        }
        return false;
    }
}    

需要注意的是 該校驗(yàn)器泛型定義中 第一個(gè)是校驗(yàn)注解的名字 第二個(gè)是校驗(yàn)屬性的類(lèi)型 isvalid返回是否校驗(yàn)成功。

屬性聯(lián)合校驗(yàn)

如果我們要定義一個(gè)對(duì)對(duì)象的聯(lián)合校驗(yàn),比如說(shuō)如果有一個(gè)flag屬性,如果flag屬性為特殊值的話那么這個(gè)對(duì)象就不用校驗(yàn)其他屬性了,或者說(shuō)另一種場(chǎng)景前端輸入的密碼和重復(fù)密碼,我后端這里要校驗(yàn)一下這兩個(gè)屬性的值是否相同,不相同就定義校驗(yàn)失敗,不能操作。這種多個(gè)屬性之間聯(lián)合起來(lái)一起影響校驗(yàn)結(jié)果的聯(lián)合校驗(yàn)需要我們這樣定義:
由于校驗(yàn)注解是自定義的 我們可以把它定義到類(lèi)頭上 然后校驗(yàn)器里獲取到這個(gè)類(lèi)對(duì)象再做聯(lián)合校驗(yàn)

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
public @interface FieldMatch
{
    String message() default "{constraints.fieldmatch}";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
 
//   /**
//     * Defines several <code>@FieldMatch</code> annotations on the same element
//     *
//     * @see FieldMatch
//     */
//    @Target({TYPE, ANNOTATION_TYPE})
//    @Retention(RUNTIME)
//    @Documented
//            @interface List
//    {
//        FieldMatch[] value();
//    }

    //只能有string 類(lèi)型的參數(shù) 獲取后再轉(zhuǎn)
    String priceTypeId();

    String costPrice();

    String sellPrice();

    String stock();

    String flag();
}
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import admtic.annotation.FieldMatch;

//校驗(yàn)注解 也可以用于dto整個(gè)類(lèi) 注意這里的泛型 前面的時(shí)自定義的注解,后面那個(gè)時(shí)被校驗(yàn)的字段類(lèi)型 這個(gè)用于對(duì)象屬性的聯(lián)合校驗(yàn) 所以用object
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {
    // 只能有string 類(lèi)型的參數(shù)
    private String priceTypeId;

    private String costPrice;

    private String sellPrice;

    private String stock;

    private String flag;

    @Override
    public void initialize(final FieldMatch constraintAnnotation) {
        priceTypeId = constraintAnnotation.priceTypeId();
        costPrice = constraintAnnotation.costPrice();
        sellPrice = constraintAnnotation.sellPrice();
        stock = constraintAnnotation.stock();
        flag = constraintAnnotation.flag();
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context) {
        try {
            List<Field> fields = Arrays.asList(value.getClass().getDeclaredFields());
            Integer priceTypeIdValue = null;
            Double sellPriceValue = null;
            Double costPriceValue = null;
            Integer stockValue = null;
            Integer flagValue = null;
            fields.forEach(e->e.setAccessible(true));
            Map<String, List<Field>> group =
                    fields.stream().collect(Collectors.groupingBy(Field::getName));
            priceTypeIdValue = (Integer) group.get(priceTypeId).get(0).get(value);
            if(priceTypeIdValue == null || priceTypeIdValue < 1){
                return false;
            }
            
            flagValue = (Integer) group.get(flag).get(0).get(value);
            
            if(flagValue == -1){
                return true;
            }
            
            
            sellPriceValue = (Double) group.get(sellPrice).get(0).get(value);
            costPriceValue = (Double) group.get(costPrice).get(0).get(value);
            stockValue=(Integer) group.get(stock).get(0).get(value);
            
//            System.out.println("######");
//            System.out.println(priceTypeIdValue);
//            System.out.println(sellPriceValue);
//            System.out.println(costPriceValue);
//            System.out.println(stockValue);
//            System.out.println(flagValue);
            
            
            
            if(sellPriceValue == null || costPriceValue == null || stockValue == null || priceTypeIdValue == null){
                return false;
            }

        } catch (final Exception ignore) {
            ignore.printStackTrace();
            return false;
        }
        return true;
    }
}

在校驗(yàn)器中反射獲取屬性值 并進(jìn)行校驗(yàn)處理

校驗(yàn)結(jié)果的處理

立即返回和全部校驗(yàn)

對(duì)一個(gè)對(duì)象校驗(yàn)時(shí),比如說(shuō)我們要校驗(yàn)10個(gè)字段,第3個(gè)字段沒(méi)有通過(guò),此時(shí)我們可以選擇馬上返回前端失敗,也可以選擇把后面的字段全部校驗(yàn),拿到所有字段的校驗(yàn)結(jié)果后再返回。
對(duì)于第一種快速失敗的配置如下:

@Configuration
public class ValidatorConfiguration {
        @Bean
        public Validator validator(){
            ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                    .configure()
                    .failFast( true )
                    .buildValidatorFactory();
            Validator validator = validatorFactory.getValidator();

            return validator;
        }
    }

//failFast: true 快速失敗返回模式,false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
     .configure()
     .failFast( true )
     .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

//hibernate.validator.fail_fast: true 快速失敗返回模式,false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
     .configure()
     .addProperty( "hibernate.validator.fail_fast", "true" )
     .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();

1 handler 單獨(dú)處理

在每個(gè)controller方法中可以獲取校驗(yàn)失敗后的錯(cuò)誤信息 進(jìn)行處理 如:

    @Override
    public Result<Boolean> addMenuOne(HttpServletRequest request, @RequestBody @Validated MenuDTO dto,
            BindingResult result) {
        if (result.hasErrors()) {
            for (ObjectError error : result.getAllErrors()) {
                return Result.failed(error.getDefaultMessage());
            }
        }

2 全局處理

每個(gè)方法都加這個(gè)處理太麻煩了 我們可以把這個(gè)錯(cuò)誤跑出來(lái) 再全局處理
方法:

 @Override
    public Result<List<Month>> getCalendarList(@Validated({ calendar.class }) @RequestBody GroupDTO dto
            ) {
//        if (result.hasErrors()) {
//            for (ObjectError error : result.getAllErrors()) {
//                return Result.failed(error.getDefaultMessage());
//            }
//        }
        List<Month> months = groupService.getDefaultCalendar(dto);
        return Result.success(months);
    }

我們把bindingResult 去掉不做處理 之后 全局定義一個(gè)錯(cuò)誤處理器:

package org.dmc.b.admtic.config;

import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.dmc.common.utils.Result;
@ControllerAdvice
public class GloableExceptionHandler {
    
    private static final Logger log = LoggerFactory.getLogger(GloableExceptionHandler.class);

    @ExceptionHandler({ ConstraintViolationException.class,

            MethodArgumentNotValidException.class,

            ServletRequestBindingException.class,

            BindException.class })
    @ResponseBody
    public Result<?> handleValidationException(Exception e) {

        String msg = "";
        if (e instanceof MethodArgumentNotValidException) {

            MethodArgumentNotValidException t = (MethodArgumentNotValidException) e;
            msg = t.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.joining(","));
        } else if (e instanceof BindException) {

            BindException t = (BindException) e;
            msg = t.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.joining(","));
        } else if (e instanceof ConstraintViolationException) {

            ConstraintViolationException t = (ConstraintViolationException) e;
            msg = t.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
                    .collect(Collectors.joining(","));
        } else if (e instanceof MissingServletRequestParameterException) {

            MissingServletRequestParameterException t = (MissingServletRequestParameterException) e;
            msg = t.getParameterName() + " 不能為空";
        } else if (e instanceof MissingPathVariableException) {

            MissingPathVariableException t = (MissingPathVariableException) e;
            msg = t.getVariableName() + " 不能為空";
        } else {
            msg = "必填參數(shù)缺失";
        }
        log.warn("=========================**********=====================參數(shù)校驗(yàn)不通過(guò),msg: {}", msg);
        return Result.failed(msg);
    }
}

可以達(dá)到同樣的效果 但是又少些了很多代碼


錯(cuò)誤信息(1).png

返回前端


返回的數(shù)據(jù)

用到的注解

@ControllerAdvice

@ControllerAdvice 注解,spring3.2提供的新注解,控制器增強(qiáng),全局增強(qiáng)可以用于定義@ExceptionHandler、@InitBinder、@ModelAttribute,并應(yīng)用到所有@RequestMapping中

使用 @ControllerAdvice,不用任何的配置,只要把這個(gè)類(lèi)放在項(xiàng)目中,Spring能掃描到的地方。就可以實(shí)現(xiàn)全局異常的回調(diào)。

該注解使用@Component注解,這樣的話當(dāng)我們使用<context:component-scan>掃描時(shí)也能掃描到。

僅僅從命名上來(lái)看 advice就是通知,猜想它就是依賴一個(gè)aop實(shí)現(xiàn)。
這個(gè)后面有時(shí)間探究。
如:

@ControllerAdvice
public class MyControllerAdvice {

    /**
     * 應(yīng)用到所有@RequestMapping注解方法,在其執(zhí)行之前初始化數(shù)據(jù)綁定器
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        //可以對(duì)日期的統(tǒng)一處理
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
        //也可以添加對(duì)數(shù)據(jù)的校驗(yàn)
        //binder.setValidator();
    }

    /**
     * 把值綁定到Model中,使全局@RequestMapping可以獲取到該值
     * @param model
     */
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("author", "Magical Sam");
    }

    /**
     * 全局異常捕捉處理
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map errorHandler(Exception ex) {
        Map map = new HashMap();
        map.put("code", 100);
        map.put("msg", ex.getMessage());
        return map;
    }

}

啟動(dòng)應(yīng)用后,上述三個(gè)方法都會(huì)作用在 被 @RequestMapping 注解的方法上

@controllerAdvice源碼

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

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

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

    Class<? extends Annotation>[] annotations() default {};
}

我們可以傳遞basePackage,聲明的類(lèi)(是一個(gè)數(shù)組)指定的Annotation參數(shù)
@ControllerAdvice是一個(gè)@Component,用于定義@ExceptionHandler,@InitBinder和@ModelAttribute方法,適用于所有使用@RequestMapping方法。

Spring4之前,@ControllerAdvice在同一調(diào)度的Servlet中協(xié)助所有控制器。Spring4已經(jīng)改變:@ControllerAdvice支持配置控制器的子集,而默認(rèn)的行為仍然可以利用。

在Spring4中, @ControllerAdvice通過(guò)annotations(), basePackageClasses(), basePackages() 方法定制用于選擇控制器子集。

ControllerAdvice定義的Class是有作用范圍的,默認(rèn)情況下,什么參數(shù)都不指定時(shí)它的作用范圍是所有的范圍。ControllerAdvice提供了一些可以縮小它的處理范圍的參數(shù)。

value:數(shù)組類(lèi)型,用來(lái)指定可以作用的基包,即將對(duì)指定的包下面的Controller及其子包下面的Controller起作用。
basePackages:數(shù)組類(lèi)型,等價(jià)于value。
basePackageClasses:數(shù)組類(lèi)型,此時(shí)的基包將以指定的Class所在的包為準(zhǔn)。
assignableTypes:數(shù)組類(lèi)型,用來(lái)指定具體的Controller類(lèi)型,它可以是一個(gè)共同的接口或父類(lèi)等。
annotations:數(shù)組類(lèi)型,用來(lái)指定Class上擁有指定的注解的Controller。
下面的ControllerAdvice將對(duì)定義在com.elim.app.mvc.controller包及其子包中的Controller起作用。

@ControllerAdvice(value="com.elim.app.mvc.controller")

下面的ControllerAdvice也將對(duì)定義在com.elim.app.mvc.controller包及其子包中的Controller起作用。

@ControllerAdvice(basePackages="com.elim.app.mvc.controller")

下面的ControllerAdvice也將對(duì)定義在com.elim.app.mvc.controller包及其子包中的Controller起作用。它通過(guò)basePackageClasses指定了需要作為基包的Class,此時(shí)基包將以basePackageClasses指定的Class所在的包為準(zhǔn),即com.elim.app.mvc.controller。

@ControllerAdvice(basePackageClasses=com.elim.app.mvc.controller.Package.class)
面的ControllerAdvice將對(duì)FooController及其子類(lèi)型的Controller起作用。

@ControllerAdvice(assignableTypes=FooController.class)
下面的ControllerAdvice將對(duì)所有Class上使用了RestController注解標(biāo)注的Controller起作用。

@ControllerAdvice(annotations=RestController.class)
也可以同時(shí)指定多個(gè)屬性,比如下面的ControllerAdvice將對(duì)FooController及其子類(lèi)型的Controller起作用,同時(shí)也將對(duì)com.elim.app.mvc.controller包及其子包下面的Controller起作用。

@ControllerAdvice(assignableTypes=FooController.class, basePackages="com.elim.app.m

@ExceptionHandler

@ExceptionHandler 攔截了異常,我們可以通過(guò)該注解實(shí)現(xiàn)自定義異常處理。其中,@ExceptionHandler 配置的 value 指定需要攔截的異常類(lèi)型。
需要注意的是使用@ExceptionHandler注解傳入的參數(shù)可以一個(gè)數(shù)組,且使用該注解時(shí),傳入的參數(shù)不能相同,也就是不能使用兩個(gè)@ExceptionHandler去處理同一個(gè)異常。如果傳入?yún)?shù)相同,則初始化ExceptionHandler時(shí)會(huì)失敗

當(dāng)一個(gè)Controller中有方法加了@ExceptionHandler之后,這個(gè)Controller其他方法中沒(méi)有捕獲的異常就會(huì)以參數(shù)的形式傳入加了@ExceptionHandler注解的那個(gè)方法中

除了全局增強(qiáng)捕獲所有Controller中拋出的異常,我們也可以使用接口的默認(rèn)方法(1.8)

public interface DataExceptionSolver {
    @ExceptionHandler
    @ResponseBody
    default Object exceptionHandler(Exception e){
        try {
            throw e;
        } catch (SystemException systemException) {
            systemException.printStackTrace();
            return WebResult.buildResult().status(systemException.getCode())
                    .msg(systemException.getMessage());
        } catch (Exception e1){
            e1.printStackTrace();
            return WebResult.buildResult().status(Config.FAIL)
                    .msg("系統(tǒng)錯(cuò)誤");
        }
    }
}

但這種方法即依賴1.8 又需要controller實(shí)現(xiàn)接口 不如全局爽。
還有更加偷雞的方法時(shí)直接定義到handler中 @ExceptionHandler這個(gè)只會(huì)是在當(dāng)前的Controller里面起作用
當(dāng)一個(gè)Controller中有多個(gè)@ExceptionHandler注解出現(xiàn)時(shí),那么異常被哪個(gè)方法捕捉呢?這就存在一個(gè)優(yōu)先級(jí)的問(wèn)題,@ExceptionHandler的優(yōu)先級(jí)是:在異常的體系結(jié)構(gòu)中,哪個(gè)異常與目標(biāo)方法拋出的異常血緣關(guān)系越緊密,就會(huì)被哪個(gè)捕捉到

@Controller
@RequestMapping(value = "exception")
public class ExceptionHandlerController {
 
      @ExceptionHandler({ ArithmeticException.class })
      public String handleArithmeticException(Exception e) {
            e.printStackTrace();
            return "error";
    }
 
      @RequestMapping(value = "e/{id}", method = {RequestMethod.GET })
      @ResponseBody
   public String testExceptionHandle(@PathVariable(value = "id") Integer id) {
        System.out.println(10 / id);
        return id.toString();
    }
}

當(dāng)訪問(wèn)exception/e/0的時(shí)候,會(huì)拋出ArithmeticException異常,@ExceptionHandler就會(huì)處理并響應(yīng)error.jsp

@ResponseStatus

這里還有一個(gè)注解@ResponseStatus 可以將某種異常映射為HTTP狀態(tài)碼
如:

@Controller
@RequestMapping(value = "status")
public class ResponseStatusController {

    /**
    * ResponseStatus修飾目標(biāo)方法,無(wú)論它執(zhí)行方法過(guò)程中有沒(méi)有異常產(chǎn)生,用戶都會(huì)得到異常的界面。而目標(biāo)方法正常執(zhí)行
    * @param id
    * @return
    */
    @RequestMapping(value = "e2/{id}", method = { RequestMethod.GET })
    @ResponseStatus(value = HttpStatus.BAD_GATEWAY)
    @ResponseBody
    public String status2(@PathVariable(value = "id") Integer id) {
        System.out.println(10 / id);
        return id.toString();
    }

}

這樣前端即使請(qǐng)求成功了也會(huì)返回 502的狀態(tài)碼
那么 將這個(gè)注解也放入controller增強(qiáng)的代碼中

/**
     * 捕獲CustomException
     * @param e
     * @return json格式類(lèi)型
     */
    @ResponseBody
    @ExceptionHandler({CustomException.class}) //指定攔截異常的類(lèi)型
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //自定義瀏覽器返回狀態(tài)碼
    public Map<String, Object> customExceptionHandler(CustomException e) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", e.getCode());
        map.put("msg", e.getMsg());
        return map;
    }

    /**
     * 捕獲CustomException
     * @param e
     * @return 視圖
     */
//    @ExceptionHandler({CustomException.class})
//    public ModelAndView customModelAndViewExceptionHandler(CustomException e) {
//        Map<String, Object> map = new HashMap<>();
//        map.put("code", e.getCode());
//        map.put("msg", e.getMsg());
//        ModelAndView modelAndView = new ModelAndView();
//        modelAndView.setViewName("error");
//        modelAndView.addObject(map);
//        return modelAndView;
//    }

那么單獨(dú)多定義幾個(gè)處理方法 即可控制返回的錯(cuò)誤碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容