显示导航

使用命令对象处理表单数据

365bet地区本指南将说明如何使用命令对象来验证365bet地区应用程序中的输入数据

s马修·莫斯(Matthew Moss)

365bet地区版本 3.3.1

365bet地区培训

365bet地区培训由创建并积极维护365bet地区框架的人们开发和交付

入门

在本指南中,您将使用命令对象通过自动类型转换自定义验证和简单的错误处理来处理表单提交

您将需要什么

要完成本指南,您将需要以下内容

  • 花些时间在你手上

  • 体面的文本编辑器或IDE

  • 安装了JDK或更高版本JAVA首页适当配置

如何完成指南

要开始,请执行以下操作

要么

365bet地区指南存储库包含两个文件夹

  • 初始初始项目通常是一个简单的365bet地区应用程序,其中包含一些其他代码,可以帮助您快速入门

  • 完成一个完整的示例它是按照指南中介绍的步骤进行操作并将这些更改应用于文档的结果。初始

要完成指南,请转到初始

  • 光盘进入grails引导命令对象并形成初始形式

并按照下一节中的说明进行操作

您可以直接前往完成的例子如果你光盘进入grails指导命令对象和表单完成

什么是命令对象

如中所述365bet地区文档,

命令对象是一个类,通常与数据绑定结合使用,以允许验证可能不适合现有域类的数据仅当将类用作动作的参数时,才将其视为命令对象

而域类能够用作命令对象,其他文件中定义的非域类甚至使用该类与控制器在同一文件中定义的类也可以用作命令对象

编写申请

本指南将引导您完成创建使用命令对象的操作的控制器的过程,通过这些命令对象,您将学习如何以最少的样板内容轻松安全地处理表单输入。

创建控制器

在使用命令对象创建任何动作之前,我们需要一个控制器创建一个PlayerController类与现有域对象一起使用播放器.

$ ./grailsw创建控制器演示播放器
第一次运行命令应用程序依赖项将从Internet上下载。后续调用将更快

命令的输出将如下所示

创建365bet地区应用程序控制器演示PlayerController groovy创建src测试Groovy演示PlayerControllerSpec groovy

现在,您将有一个新的几乎为空的控制器,名为PlayerController常规在里面365bet地区应用控制器演示目录更新指数操作并添加节目动作看起来像这样

365bet地区应用程序控制器演示PlayerController groovy
演示进口 org springframework http HttpStatus

 PlayerController {

    定义 指数响应玩家列表参数模型: [playerCount玩家人数定义 节目玩家玩家回应玩家

为您提供了域类视图和一些示例数据。此时,您应该能够运行该应用程序并导航到http本地主机播放器要查看玩家列表并单击玩家名称以查看该玩家,但是其他操作(例如,创建,保存,编辑,更新,删除,删除)都不会起作用,除非您编写了这些操作

您随时可以通过键入以下内容启动应用程序gradlew bootRun在命令行上,它正在运行并准备就绪,当您看到

365bet地区在环境开发中运行在http localhost上的365bet地区应用程序

通过按停止应用程序Ctrl C.

实施创建和保存操作

接下来,我们添加创建新玩家的功能PlayerController添加创建行动

365bet地区应用程序控制器演示PlayerController groovy
禁止警告(['FactoryMethodName', '365bet地区365bet地区MassAssignment'])
定义 创建响应玩家参数

尽管此操作确实利用了播放器类的实例播放器 不是命令对象,因为它不是操作的自变量这应该是有道理的,因为操作的目的是创建新的播放器对象,因此不需要任何形式或输入处理

从玩家列表http本地主机播放器索引点击新玩家按钮以查看表单以创建位于以下位置的新播放器http localhost播放器创建如果要在表单中输入值,然后单击创建按钮,您会得到一个找不到网页保存动作尚未定义现在将其添加到PlayerController.

    定义 保存播放器播放器// ...
    }

这里播放器 365bet地区一个命令对象,因为它是操作的一个参数在后台365bet地区会识别出这一点,并在必要时重写您的操作以获取现有记录以将输入数据绑定到命令对象上以执行依赖项注入并验证该对象所有这些任务都可以手动完成但是通过使用命令对象,所有这些行为都是自动的,并且使您的代码变得如此整洁

可以说这里最好的功能是数据绑定使用命令对象获得的自动行为的一部分,在创建页面上查看从编译后生成的HTML源代码创建gsp并寻找

标签

<> 行动="玩家保存" 方法="发布" >
    <> ="形成">
        <> ='必填字段'>
          <> 对于='那么'>name<> ='必要指标'>*
          <> 类型="文本" 那么="那么" ="" 需要="" ID="那么" />
        
<> ='必填字段'> <> 对于='游戏'>游戏<> ='必要指标'>* <> 类型="文本" 那么="游戏" ="" 需要="" ID="游戏" />
<> ='现场包含'> <> 对于='区域'>区域 <> 类型="文本" 那么="区域" ="" ID="区域" />
<> ='必填字段'> <> 对于=''>获胜<> ='必要指标'>* <> 类型="" 那么="" ="0" 需要="" ="0" ID="" /> <> ='必填字段'> <> 对于='损失'>损失<> ='必要指标'>* <> 类型="" 那么="损失" ="0" 需要="" ="0" ID="损失" /> <> ="纽扣"> <> 类型="提交" 那么="创建" ="保存" ="创建" ID="创建" /> 形成

标签名称与域类的属性完全匹配播放器.

365bet地区应用程序域演示Player groovy
演示 播放器 {
    那么游戏区域整型0
    整型损失0

    静态的约束名称空白: , 独特: 真正游戏空白: 区域可为空: 真正: 0损失: 0
    }
}

数据绑定将采用以下形式提交的字符串值:那么, 游戏区域如果设置并在命令对象中设置这些属性播放器表单提交的字符串值损失也将设置在播放器自动类型转换为整型对域类属性和输入字段使用相同名称的约定极大地简化了编码,并且应用程序开发视图字段预先填充了域类对象的值,而命令对象则填充了提交的表单数据,几乎不需要开发人员

在绑定数据验证之后,因为这是一个域类,所以将完成验证,该类中设置了任何约束约束将检查块,这使我们有机会添加简单的错误检查PlayerController常规

进口 org springframework http HttpStatus

然后更新保存的行动PlayerController.

365bet地区应用程序控制器演示PlayerController groovy
定义 保存播放器播放器如果播放器空值渲染状态找不到HttpStatus返回
    }

    如果播放器hasErrors响应播放器错误视图: '创建'
        返回
    }
}

现有产生365bet地区文件包含Javascript以进行尽可能多的客户端窗体验证要查看365bet地区服务器端验证和错误检查,请尝试使用卷曲365bet地区在365bet地区应用程序运行时,在命令行上键入

卷曲请求POST表单名称Bob Smith表单获胜表单损失abc HTTP本地播放器保存JSON

您应该会收到很好的显示以下响应

错误对象演示Player字段损失拒绝值abc消息,属性损失类型不匹配,对象演示Player字段游戏拒绝值null消息,类class demo Player的属性游戏不能为null

我们将通过功能测试来验证

功能测试涉及对正在运行的应用程序发出HTTP请求并验证结果行为

我们使用365bet地区Rest Client Builder 365bet地区插件365bet地区如果您使用以下命令创建365bet地区应用,则会为您添加此插件:休息api轮廓

我们使用网络配置文件创建了该应用程序,但是很容易将testCompile依赖项添加到我们的构建文件中将下一行添加到依赖项块中

建立gradle
依赖项testCompile"组织grails grails数据存储区其余客户端"
}
src集成测试groovy演示演示PlayerControllerSpec groovy
演示进口 spock lang规格
进口 grails插件rest client RestBuilder
进口 grails测试mixin集成集成

禁止警告(['线长', '方法名称'])
积分
 PlayerControllerFuncSpec 延伸规格定义 '测试保存验证'() {
        给定RestBuilder的休息RestBuilder的什么时候:
        定义休息休息"HTTP本地主机${服务器端口}玩家保存") { (1)接受'应用程序json') (2)内容类型'应用程序json') (3)json名称'鲍勃·史密斯'42损失'abc'
            }
        }

        然后状态422 (4)分别为json错误大小2响应json错误找到领域'损失'信息'财产损失类型不匹配'响应json错误找到领域'游戏'信息'类class demo Player的属性游戏不能为null'
    }
}
1 服务器端口365bet地区属性是自动注入的,它包含功能测试期间运行365bet地区应用程序的随机端口
2 如果您的客户端接受JSON,则最好设置“接受Http标头”
3 如果您要发送JSON有效负载,则应将Content Type Http Header设置为application json
4 由于命令对象验证失败,因此服务器返回Http Status Unprocessable Entity

现在,完成了数据绑定验证和错误检查,然后更新保存实际保存对象并响应表单提交的操作这样就完成了操作

定义 保存播放器播放器如果播放器空值渲染状态找不到HttpStatus返回
    }

    如果播放器hasErrors响应播放器错误视图: '创建'
        返回玩家保存齐平: 真正请求withFormat形式multipartForm重定向播放器'*'回应玩家状态HttpStatus已创建

现在,您应该能够成功创建新玩家并将其保存到数据库中

此处提供的示例控制器代码保存365bet地区和本指南中的其他操作有意保持简单,以便专注于命令对象。您很可能希望在操作中具有其他功能。构建自己的控制器时,一个很好的起点是使用365bet地区生成初始控制器生成控制器脚本

grailsw生成控制器演示播放器

实施编辑和更新操作

接下来,让我们添加更新现有播放器的功能添加编辑更新采取行动PlayerController您应该注意,这些与创建保存行动

    定义 编辑玩家玩家回应玩家定义 更新播放器播放器如果播放器空值渲染状态找不到HttpStatus返回
        }

        如果播放器hasErrors响应播放器错误视图: '编辑'
            返回玩家保存齐平: 真正请求withFormat形式multipartForm重定向播放器'*'回应玩家状态HttpStatus确定

编辑动作以播放器365bet地区命令对象365bet地区提取现有对象播放器对象会自动加载,并且该对象的属性将作为表单的初始值加载到编辑gsp更新动作与保存错误会重定向回编辑而不是创建成功的HTTP状态是而不是已建立.

假设将在其他地方管理玩家的获胜和失败计数,则已经从表格中删除了获胜和失败字段。编辑gsp在运行应用程序时,转至玩家列表,然后单击其中一个玩家名称。编辑按钮将显示不允许您更改赢彩次数的表格

但这还不足以从表单中删除输入字段不能阻止那些字段不会被更改聪明的用户可以通过其他方式提交表单,以更改这些值

curl请求GET http localhost播放器显示json id游戏大流行丢失名称Alexis Barnett地区EAST赢得卷曲请求POST形式id形式赢得形式丢失http localhost播放器更新json

第二次之后卷曲指挥员亚历克西斯·巴内特(Alexis Barnett)将只有两次胜利和失败,而不是她应该拥有的胜利和失败

解决此问题的一种方法是避免使用播放器命令对象,以避免自动填充属性,而是手动管理所有加载绑定和验证,您可以自定义操作的行为,而忽略任何修改获胜和失败属性的尝试

但是命令对象不是必须是域类,而可以使用任何类。PlayerController常规以下PlayerControllerclass请注意,玩家信息与形式s相匹配提交表单时的标签,表单值将绑定到名称匹配的命令对象的属性

 玩家信息 {
    那么游戏区域

改变更新使用动作玩家信息作为命令对象加载适当的播放器实例,然后使用命令对象的属性更新并保存

    定义 更新PlayerInfo信息播放器播放器播放器获取参数ID如果播放器空值渲染状态找不到HttpStatus返回播放器属性信息播放器保存齐平: 真正

        如果播放器hasErrors响应播放器错误视图: '编辑'
            返回请求withFormat形式multipartForm重定向播放器'*'回应玩家状态HttpStatus确定

以来玩家信息确实包含要么损失即使为这些名称提交了值,也不会从表单提交中接受这些值

curl请求GET http本地主机显示json ID名称Catherine Newton游戏大镰刀区域WEST赢得损失curl请求POST表单ID表单名称June Smith表单游戏国际象棋表单区域NORTH表单WIN表单丢失HTTP本地玩家更新json curl请求GET HTTP本地主机show json id名称六月史密斯游戏国际象棋地区北方赢得损失

请注意那么, 游戏区域属性已全部更新,但损失尽管我们竭尽所能,但物业并没有改变玩家信息我们限制了命令对象从表单提交中接受哪些字段

创建一个单元测试以验证此行为

src测试groovy演示PlayerControllerSpec groovy
演示进口 spock lang规格
进口 免费测试gorm DomainUnitTest
进口 365bet地区测试Web控制器ControllerUnitTest

禁止警告(['方法名称', 'DuplicateListLiteral', 'DuplicateNumberLiteral', '线长'])
 PlayerControllerSpec 延伸规格实施ControllerUnitTestDomainUnitTest {

    定义 "测试更新"() {
        什么时候:
        定义播放器播放器那么: '塞尔吉奥', 游戏: 'XCOM敌人未知', 区域: '西班牙', : 3, 损失: 2玩家保存参数ID玩家ID参数名称'塞尔吉奥·德尔阿莫'参数游戏'XCOM'参数区'美国'参数胜4控制器更新(1)

        然后: '响应模型没有错误'模型播放器hasErrors: '播放器属性已正确更新'模特儿名字'塞尔吉奥·德尔阿莫'模型玩家游戏'XCOM'模型播放器区域'美国'

        : '命令对象中不存在的属性尚未修改'模型玩家获胜3模型玩家损失2
    }
}
1 调用更新方法的无参数版本。参数map数据绑定会将这些值映射到Command对象,这将模拟运行时发生的情况

目前的解决方案虽然缺乏,因为玩家信息现在是命令对象,而不是播放器验证直到调用才完成玩家保存最好早做验证,因为验证早在播放器是命令对象

要解决此问题,请添加约束阻止玩家信息定义命令对象类时玩家信息在里面PlayerController常规365bet地区文件365bet地区将其识别为相关的命令对象类,并使该类能够被验证玩家信息并添加约束

365bet地区应用程序控制器演示PlayerController groovy
 玩家信息 {
    那么游戏区域静态的约束名称空白: 游戏空白: 区域可为空: 真正
    }
}

创建一个单元测试以验证命令对象中定义的约束

src测试groovy演示PlayerInfoSpec groovy
演示进口 spock lang规格

禁止警告(['方法名称', 'DuplicateListLiteral'])
 PlayerInfoSpec 延伸规格定义 "测试PlayerInfo区域可以为null"() {
        期望:
        玩家信息区域: 空值验证'区域'])
    }

    定义 "测试PlayerInfo名称可以为空"() {
        什么时候:
        定义playerInfo玩家信息那么: '')

        然后playerInfo验证'那么'playerInfo错误'那么''空白'

        什么时候playerInfo玩家信息那么: 空值)

        然后playerInfo验证'那么'playerInfo错误'那么''可为空'
    }

    定义 "测试PlayerInfo游戏可以为空白"() {
        什么时候:
        定义playerInfo玩家信息游戏: '')

        然后playerInfo验证'游戏'playerInfo错误'游戏''空白'

        什么时候playerInfo玩家信息游戏: 空值)

        然后playerInfo验证'游戏'playerInfo错误'游戏''可为空'
    }
}

修改更新再次执行操作,在尝试保存之前返回错误检查

365bet地区应用程序控制器演示PlayerController groovy
定义 更新PlayerInfo信息如果info hasErrors响应信息错误视图: '编辑'
        返回玩家玩家玩家获取参数ID如果播放器空值渲染状态找不到HttpStatus返回播放器属性信息播放器保存齐平: 真正请求withFormat形式multipartForm重定向播放器'*'回应玩家状态HttpStatus确定

命令对象类可以在其他地方定义,例如在src文件夹层次结构它们不必与使用它们的控制器位于同一文件中,这对于保持关注点分离或仅使命令对象类可用于多个控制器可能很有用

365bet地区如果您的命令对象类与使用它的控制器在同一源文件中定义,365bet地区将自动使它成为有效期允许使用静态约束如果您在其他地方定义该类并需要约束您需要添加一个实施条款如下所示

 玩家信息 实施grails验证有效那么游戏区域静态的约束名称空白: 游戏空白: 区域可为空: 真正
    }
}

运行应用程序

要运行该应用程序,请使用gradlew bootRun命令将在端口上启动应用程序

您需要365bet地区帮助吗

OCI赞助了本指南的创建OCI提供了几种365bet地区服务:

免费咨询

OCI 365bet地区团队包括365bet地区联合创始人Jeff Scott Brown和Graeme Rocher检查我们的365bet地区课程并向发展和维护365bet地区的工程师学习

Grails OCI团队