2.2 处理表单提交
2.2 处理表单提交
仔细看一下视图中的<form>
标签,你将会发现它的method
属性被设置成了POST
。除此之外,<form>
并没有声明action
属性。这意味着当表单提交的时候,浏览器会收集表单中的所有数据,并以HTTP POST
请求的形式将其发送至服务器端,发送路径与渲染表单的GET
请求路径相同,也就是“/design
”。
因此,在该POST请求的接收端,我们需要有一个控制器处理方法。在DesignTacoController中,我们会编写一个新的处理器方法来处理针对“/design”的POST请求。
在程序清单2.2中,我们曾经使用@GetMapping注解声明showDesignForm()方法要处理针对“/design”的HTTP GET请求。与@GetMapping处理GET请求类似,我们可以使用@PostMapping来处理POST请求。为了处理taco设计的表单提交,在DesignTacoController中添加程序清单2.4所述的processDesign()方法。
程序清单2.4 使用@PostMapping来处理POST请求
1 |
|
如processDesign()方法所示,@PostMapping与类级别的@RequestMapping协作,指定processDesign()方法要处理针对“/design”的POST请求。我们所需要的正是以这种方式处理taco艺术家的表单提交。
当表单提交的时候,表单中的输入域会绑定到Taco对象(这个类会在下面的程序清单中进行介绍)的属性中,该对象会以参数的形式传递给processDesign()。从这里开始,processDesign()就可以针对Taco对象采取任意操作了。
程序清单2.5 定义taco设计的领域对象
1 | package tacos; |
我们可以看到,Taco是一个非常简单的Java领域对象,其中包含了几项属性。与Ingredient类似,Taco类也添加了@Data注解,会在编译期自动生成必要的JavaBean方法,所以这些方法在运行期是可用的。
我们可以看到,Taco是一个非常简单的Java领域对象,其中包含了几项属性。与Ingredient类似,Taco类也添加了@Data注解,会在编译期自动生成必要的JavaBean方法,所以这些方法在运行期是可用的。
回过头来再看一下程序清单2.3中的表单,你会发现其中包含多个checkbox元素,它们的名字都是ingredients,另外还有一个名为name的文本输入元素。表单中的这些输入域直接对应Taco类的ingredients和name属性。
表单中的name
输入域只需要捕获一个简单的文本值。因此,Taco
的name
属性是String
类型的。配料的复选框也有文本值,但是用户可能会选择一个或多个,所以它们所绑定的ingredients
属性是一个List<String>
,能够捕获选中的每种配料。
processDesign()方法对Taco对象没有执行任何操作。实际上,这个方法什么都没做。现在,这样是可以的。到第3章,我们将会添加一些持久化的逻辑,将提交的Taco保存到一个数据库中。
与showDesignForm()方法类似,processDesign()最后也返回了一个String类型的值。同样与showDesignForm()相似,返回的这个值代表了一个要展现给用户的视图。但是,区别在于processDesign()返回的值带有“redirect:”前缀,表明这是一个重定向视图。更具体地讲,它表明在processDesign()完成之后,用户的浏览器将会重定向到相对路径“/order/current”。
这里的想法是在创建完taco后,用户将会被重定向到一个订单表单页面,在这里用户可以创建一个订单,将他们所创建的taco快递过去。但是,我们现在还没有处理“/orders/current”请求的控制器。
根据已经学到的关于@Controller、@RequestMapping和@GetMapping的知识,我们可以很容易地创建这样的控制器。它应该如程序清单2.6所示。
程序清单2.6 展现taco订单表单的控制器
1 | package tacos.web; |
在这里,我们再次使用Lombok @Slf4j注解在运行期创建一个SLF4J Logger对象。稍后,我们将会使用这个Logger记录所提交订单的详细信息。
类级别的@RequestMapping指明这个控制器的请求处理方法都会处理路径以“/orders”开头的请求。当与方法级别的@GetMapping注解结合之后,它就能够指定orderForm()方法,会处理针对“/orders/current”的HTTP GET请求。
orderForm()方法本身非常简单,只是返回了一个名为orderForm的逻辑视图名。在第3章学习完如何将所创建的taco保存到数据库之后,我们将会重新回到这个方法并对其进行修改,用一个Taco对象的列表来填充模型并将其放到订单中。
orderForm视图是由名为orderForm.html的Thymeleaf模板来提供的,如程序清单2.7所示。
程序清单2.7 一个taco订单表单视图
1 |
|
从很大程度上来讲,orderForm.html就是典型的HTML/Thymeleaf内容,不需要过多关注。但是,需要注意一点,这里的<form>
标签和程序清单2.3中的<form>
标签有所不同,它指定了一个表单的action。如果不指定action,那么表单将会以HTTP POST的形式提交到与展现该表单相同的URL上。在这里,我们明确指明表单要POST提交到“/orders”上(使用Thymeleaf的@{…}操作符指定相对上下文的路径)。
因此,我们需要在OrderController中添加另外一个方法,以便于处理针对“/orders”的POST请求。我们在第3章才会对订单进行持久化,在此之前,我们让它尽可能简单,如程序清单2.8所示。
程序清单2.8 处理taco订单的提交
1 |
|
当调用processOrder()方法处理所提交的订单时,我们会得到一个Order对象,它的属性绑定了所提交的表单域。Order与Taco非常相似,是一个非常简单的类,其中包含了订单的信息,如程序清单2.9所示。
程序清单2.9 taco订单的领域对象
1 | package tacos; |
现在,我们已经开发了OrderController和订单表单的视图,接下来我们可以尝试运行一下。打开浏览器并访问http://localhost:8080/design,为taco选择一些配料,并点击“Submit Your Taco”按钮,将会看到如图2.3所示的一个表单。
填充表单的一些输入域并点击“Submit order”按钮。请关注应用的日志来查看你的订单信息。在我尝试运行的时候,日志条目如下所示(为了适应页面的宽度,重新进行了格式化):
1 | Order submitted: Order(name=Craig Walls,street1=1234 7th Street, |
如果仔细查看上述测试订单的日志,就会发现尽管processOrder()方法完成了它的工作并处理了表单提交,但是它让一些错误的信息混入了进来。表单中的大多数输入域包含的可能都是不正确的信息。我们接下来添加一些校验,确保所提交的数据至少与所需的信息比较接近。