前面几章实现了在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();
}
}
}

四、运行效果

  

小结

  本章实现了对请求参数的后台校验,当然也可以在前端提前进行校验,但后端的校验一般必不可少。

  源码下载地址:https://idlestudio.ctfile.com/fs/14960372-384755438

  本文原文地址:https://www.cnblogs.com/lyosaki88/p/idlewow_7.html

  项目交流群:329989095

从零开始实现放置游戏(七)——实现挂机战斗(5)RMS系统后台参数校验的更多相关文章

  1. 从零开始实现放置游戏(十)——实现战斗挂机(1)hessian服务端搭建

    前面实现RMS系统时,我们让其直接访问底层数据库.后面我们在idlewow-game模块实现游戏逻辑时,将不再直接访问底层数据,而是通过hessian服务暴露接口给表现层. 本章,我们先把hessia ...

  2. 从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件

    前两张,我们已经实现了登陆界面和游戏的主界面.不过游戏主界面的数据都是在前端写死的文本,本章我们给game模块添加websocket组件,实现前后端通信,这样,前端的数据就可以从后端动态获取到了. 一 ...

  3. 从零开始实现放置游戏(六)——实现挂机战斗(4)导入Excel数值配置

    前面我们已经实现了在后台管理系统中,对配置数据的增删查改.但每次添加只能添加一条数据,实际生产中,大量数据通过手工一条一条添加不太现实.本章我们就实现通过Excel导入配置数据的功能.这里我们还是以地 ...

  4. 从零开始实现放置游戏(六)——实现后台管理系统(4)Excel批量导入

    前面我们已经实现了在后台管理系统中,对配置数据的增删查改.但每次添加只能添加一条数据,实际生产中,大量数据通过手工一条一条添加不太现实.本章我们就实现通过Excel导入配置数据的功能.这里我们还是以地 ...

  5. 微信小程序从零开始开发步骤(七)引入外部js 文件

    上一章讲到小程序页面的四种常见的跳转的方法,这一章写如何引入一个外部的js文件,既utils文件夹的用处,其实步骤很简单: 1:准备好外部想要引入的外部文件,命名为util.js,并且填充固定的文件内 ...

  6. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(二)-Hexo参数设置

    前言 前文手把手教从零开始在GitHub上使用Hexo搭建博客教程(一)-附GitHub注册及配置介绍了github注册.git相关设置以及hexo基本操作. 本文主要介绍一下hexo的常用参数设置. ...

  7. Cocos2D iOS之旅:如何写一个敲地鼠游戏(七):弹出地鼠

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  8. (NO.00005)iOS实现炸弹人游戏(七):游戏数据的序列化表示

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 用plist列表文件来表示游戏数据 因为在这个炸弹人游戏中有很多 ...

  9. 架构师小跟班:教你从零开始申请和配置七牛云免费OSS对象存储(不能再详细了)

    背景 之前为了练习Linux系统使用,在阿里云上低价买了一台服务器(网站首页有活动链接,传送门),心里想反正闲着也是闲着,就放了一个网站上去.现在随着数据越来越多,服务器空间越来越吃紧,我就考虑使用七 ...

随机推荐

  1. jQuery memory&unique&stopOnfalse

    memory:保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred). 回调函数是从异步队列Deferred分离出来的,所以很多的接口设计都是为了契合De ...

  2. Android开发小知识

    修改Android app图标(Android Studio) 1.  res\drawable 放置icon.png(此图片是你需要修改的图标); 2.  修改AndroidManifest.xml ...

  3. JDBC数据源DBCP源代码情景分析

    在之前的一篇博文从JDBC到commons-dbutils 中,我曾经写到,对于获取数据库连接,有一个解决方案,那就是数据源.业界用到的比较普遍的开源数据源解决方案有很多,DBCP是其中一种,今天,我 ...

  4. GDI+与WPF中的颜色简析

    原文:GDI+与WPF中的颜色简析 --------------------------------------------------------------------------------引用 ...

  5. 使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等

    原文:使WPF程序应用预置的控件风格, 如Aero, Luna, Royale, Classic等      WPF预设有Aero, Classic, Luna, Royale主题, WPF程序会根据 ...

  6. uml系列(七)——互动图

    互动图uml描述如何对象的描述在系统交互动作 . 废话不多说,还是来张图: 概念          交互图,主要描写叙述的是系统中的一组对象的消息的传递的.为对象间的交互定义了一个可视的表示方法. 构 ...

  7. 中国新超算彻底告别进口CPU 国产芯片已可与国外抗衡

    日前,飞腾已经完成FT-2000plus服务器CPU的研制工作,飞腾公司的合作伙伴正在积极研发相应的整机产品.FT-2000plus这款芯片是以FT2000为基础的改进版本,虽然在单核性能上和Inte ...

  8. Rust 2017 调查报告:学习曲线是最大痛点(最大的问题是这门语言太偏底层了,现在做底层的少了。还有C这个绕不过去的存在)

    Rust 官方在社区上做了一次调查,以了解用户如何看待 Rust 的发展.调查共收到 5368 份回复,其中有 大约 2/3 的是 Rust 用户,剩下的 1/3 是非 Rust 用户,调查结果如下. ...

  9. Emgu-WPF学习使用 - 颜色映射

    原文:Emgu-WPF学习使用 - 颜色映射 string sFile = ""; if (!String.IsNullOrEmpty(AppConstUtils.GDefault ...

  10. 一个RPC的demo (good)

    从下面的例子中可以看到,Consumer(client)的代码中引用了Provider部分的class,本例中是 com.provider.EchoServiceImpl和com.provider.E ...