前面几章实现了在RMS系统中进行数据的增删查改以及通过Excel批量导入。但仍有遗留的问题,比如在新增或编辑时,怪物的生命值、护甲等数据我们可以输入负值,这种数据是不合理且没有意义的。本章我们就实现服务端对参数的校验。
一、添加依赖项
在rms模块的pom.xml中,添加校验组件的依赖项(注意:之前的组件我们都引用了最新版本。但因hibernate-validator的最新版本6.xx+中引用的el-api.jar有冲突,无法用maven插件启动,所以这里使用5.1.1版本):
<!-- 参数校验 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.1.Final</version> </dependency>
这个组件本身提供了一些注解,@NotNull, @NotBlank, @Min等等,来对模型进行校验,但错误提示不够好,默认通用的错误提示无法明确知道是哪个字段报错。如果为每个字段添加一个提示语,又非常繁琐,所以我们这里稍加改动,在util模块做一个通用的校验工具包。
在util模块的pom.xml中添加依赖:
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> <scope>provided</scope> </dependency>
二、添加自定义注解及提示信息
以非空校验为例,在util模块中新建包com.idlewow.util.validation.annotaion,在此包下新建一个注解类NotBlank.java如下:
package com.idlewow.util.validation.annotation; import com.idlewow.util.validation.validator.NotBlankValidator; import javax.validation.Constraint; import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; 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; @Documented @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @ReportAsSingleViolation @Constraint(validatedBy = NotBlankValidator.class) @NotNull public @interface NotBlank { String field() default ""; String message() default "{field.not.blank.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { NotBlank[] value(); } }
注解有了,还需要一个对应的检验器,新建包com.idlewow.util.validation.validator,并在此包下新建类NotBlankValidator如下:
package com.idlewow.util.validation.validator; import com.idlewow.util.validation.annotation.NotBlank; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class NotBlankValidator implements ConstraintValidator<NotBlank, CharSequence> { public NotBlankValidator() { } public void initialize(NotBlank annotation) { } @Override public boolean isValid(CharSequence charSequence, ConstraintValidatorContext constraintValidatorContext) { if (charSequence == null) { return false; } else { return charSequence.toString().trim().length() > 0; } } }
另外,在对模型进行校验时,不同场景下的需求不同。比如,在新增时,因为主键由数据库自增,无需添加主键;编辑时,则必须指定主键ID,对其进行非空校验。因此,我们在com.idlewow.util.validation包下在新建一个对校验分组的类ValidateGroup:
package com.idlewow.util.validation; import javax.validation.groups.Default; import java.io.Serializable; public class ValidateGroup implements Serializable { public interface Create extends Default { } public interface Update extends Default { } }
最后,我们在util模块的resource资源目录下添加提示信息的资源文件ValidationMessages.properties,
#common invalid message field.not.blank.message={field}不能为空 field.not.null.message={field}不能为NULL field.size.message={field}的长度应为{min}至{max}之间 field.min.message={field}不能小于{value} field.max.message={field}不能大于{value} field.range.message={field}的大小应为{min}至{max}之间 field.positive.message={field}必须是正数 field.negative.message={field}必须是负数
三、参数校验注解的使用
首先,我们需要在需要校验的模型上加上注解,此处以怪物模型为例:
package com.idlewow.mob.model; import com.idlewow.common.model.BaseModel; import com.idlewow.util.validation.annotation.NotBlank; import com.idlewow.util.validation.annotation.NotNull; import com.idlewow.util.validation.annotation.Positive; import lombok.Data; import lombok.EqualsAndHashCode; import java.io.Serializable; @Data @EqualsAndHashCode(callSuper = true) public class MapMob extends BaseModel implements Serializable { @NotBlank(field = "主键id", groups = ValidateGroup.Update.class) private String id; @NotBlank(field = "怪物名称") private String name; @NotBlank(field = "地图id") private String mapId; @NotBlank(field = "地图名称") private String mapName; @NotNull(field = "阵营") private Integer faction; @NotNull(field = "怪物种类") private Integer mobClass; @NotNull(field = "怪物类型") private Integer mobType; @Positive(field = "等级") private Integer level; @Positive(field = "生命值") private Integer hp; @Positive(field = "伤害") private Integer damage; @Positive(field = "护甲") private Integer amour; }
模型注解添加完毕,我们在BaseController中添加一个通用的校验方法,方便在各个Controller中调用:
public abstract class BaseController { ...... ...... @Autowired protected Validator validator; ...... ...... protected CommonResult validate(Object object, Class... classes) { Set<ConstraintViolation<Object>> set = validator.validate(object, classes); if (set != null && set.size() > 0) { ConstraintViolation constraintViolation = set.iterator().next(); return CommonResult.fail(constraintViolation.getMessage()); } return CommonResult.success(); } }
在MapMobController的新增和编辑方法中,添加校验逻辑,
@Controller @RequestMapping("/manage/map_mob") public class MapMobController extends BaseController { …… …… @ResponseBody @RequestMapping(value = "/add", method = RequestMethod.POST) public Object add(@RequestBody MapMob mapMob) { try { CommonResult commonResult = this.validate(mapMob, ValidateGroup.Create.class); if (!commonResult.isSuccess()) return commonResult; mapMob.setCreateUser(this.currentUserName()); mapMobManager.insert(mapMob); return CommonResult.success(); } catch (Exception ex) { logger.error(ex.getMessage(), ex); return CommonResult.fail(); } } …… …… @ResponseBody @RequestMapping(value = "/edit/{id}", method = RequestMethod.POST) public Object edit(@PathVariable String id, @RequestBody MapMob mapMob) { try { if (!id.equals(mapMob.getId())) { return CommonResult.fail("id不一致"); } CommonResult commonResult = this.validate(mapMob, ValidateGroup.Update.class); if (!commonResult.isSuccess()) return commonResult; mapMob.setUpdateUser(this.currentUserName()); mapMobManager.update(mapMob); return CommonResult.success(); } catch (Exception ex) { logger.error(ex.getMessage(), ex); return CommonResult.fail(); } } }
四、运行效果
小结
本章实现了对请求参数的后台校验,当然也可以在前端提前进行校验,但后端的校验一般必不可少。