• 笔记28 接受请求的输入 ——处理表单


    Spittr应用有两个基本的领域概念:Spitter(应用的用户)和 Spittle(用户发布的简短状态更新)。

    在笔记25中已经对Spittle进行了简单的构建,现在对Spitter进行实现。主要是用户的注册、用户基本信息的展示。

    注册的时候就会涉及到对表单的处理,使用表单分为两个方面:展现表单以及处理用户通过表单提交的数据。在Spittr应用中,我们需要有个表单让新用户进行注册。SpitterController是一个新的控制器,目前只有一个请求处理的方法来展现注册表单。

    1.首先构建数据访问的Repository。为了实现解耦以及避免 陷入数据库访问的细节之中,我们将Repository定义为一个接口,并在稍后实现它我们只需要一个能够获取Spitter对象的Repository,如下所示的SpitterRepositorys.java

    1 package myspittr.data;
    2 
    3 import myspittr.spitter.Spitter;
    4 
    5 public interface SpitterRepositorys {
    6     Spitter save(Spitter spitter);
    7 
    8     Spitter findByUsername(String username);
    9 }

    2.然后创建它的实现类JdbcSpitterRepository2.java用来访问数据库,然后读取数据,但是目前还不需要对数据库进行操作,所以需要自己做一下假数据。

     1 package myspittr.data;
     2 
     3 import org.springframework.stereotype.Service;
     4 
     5 import myspittr.spitter.Spitter;
     6 
     7 @Service
     8 public class JdbcSpitterRepository2 implements SpitterRepositorys {
     9 
    10     private Spitter savedSpitter;
    11 
    12     public JdbcSpitterRepository2() {
    13     }
    14 
    15     public Spitter save(Spitter spitter) {
    16         // TODO Auto-generated method stub
    17         Spitter spitter2 = new Spitter(spitter.getUsername(), spitter.getPassword(), spitter.getFirstName(),
    18                 spitter.getLastName());
    19         this.savedSpitter = spitter2;
    20         return spitter2;
    21     }
    22 
    23     public Spitter findByUsername(String username) {
    24         // TODO Auto-generated method stub
    25         if (username.equals(savedSpitter.getUsername())) {
    26             return savedSpitter;
    27         } else {
    28             return null;
    29         }
    30 
    31     }
    32 
    33 }

    3.SpitterController.java  展现一个表单,允许用户注册该应用

     1 package spittr.web;
     2 
     3 import javax.validation.Valid;
     4 
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.stereotype.Controller;
     7 import org.springframework.ui.Model;
     8 import org.springframework.validation.Errors;
     9 import org.springframework.web.bind.annotation.PathVariable;
    10 import org.springframework.web.bind.annotation.RequestMapping;
    11 import org.springframework.web.bind.annotation.RequestMethod;
    12 
    13 import spittr.data.SpitterRepository;
    14 import spittr.spitter.Spitter;
    15 
    16 @Controller
    17 @RequestMapping("/spitter")
    18 public class SpitterController {
    19 
    20     @RequestMapping(value = "/register", method = RequestMethod.GET) // 处理对“/spitter/register”的GET请求
    21     public String showRegistrationForm() {
    22         return "registerForm";
    23     }
    24 
    25 
    26 }

    showRegistrationForm()方法的@RequestMapping注解以及 类级别上的@RequestMapping注解组合起来,声明了这个方法要处 理的是针对“/spitter/register”的GET请求。这是一个简单的方法,没有 任何输入并且只是返回名为registerForm的逻辑视图。按照我们 配置InternalResourceViewResolver的方式,这意味着将会使 用“/WEB-INF/ views/registerForm.jsp”这个JSP来渲染注册表单。 

    4.测试展现表单的控制器方法

    1     @Test
    2     public void shouldShowRegistration() throws Exception {
    3         SpitterController controller = new SpitterController();
    4         MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); // 构建MockMvc
    5         mockMvc.perform(get("/spitter/register")).andExpect(view().name("registerForm")); // 断言registerForm视图
    6     }

    这个测试方法与首页控制器的测试非常类似。它对“/spitter/register”发 送GET请求,然后断言结果的视图名为registerForm。 

    5.渲染注册表单的JSP   registerForm.jsp

     1 <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
     2 <%
     3 String path = request.getContextPath();
     4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
     5 %>
     6 
     7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
     8 <html>
     9   <head>
    10     <base href="<%=basePath%>">
    11     
    12     <title>Spitter</title>
    13     <link rel="stylesheet" type="text/css" href="<c:url value="/respurces/style.css"/>">
    14     
    15     <meta http-equiv="pragma" content="no-cache">
    16     <meta http-equiv="cache-control" content="no-cache">
    17     <meta http-equiv="expires" content="0">    
    18     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    19     <meta http-equiv="description" content="This is my page">
    20     <!--
    21     <link rel="stylesheet" type="text/css" href="styles.css">
    22     -->
    23 
    24   </head>
    25   
    26   <body>
    27     <h1>Register</h1>
    28     <form action="" method="POST">
    29         First Name:<input type="text" name="firstName"/><br>
    30         Last Name:<input type="text" name="lastName"><br>
    31         Username:<input type="text" name="username"><br>
    32         Password:<input type="password" name="password"><br>
    33         <input type="submit" value="Register"/>
    34     </form>
    35   </body>
    36 </html>

    需要注意的是:这里的<form>标签中并没有设置action属性。在这种情况下,当表单提交时,它会提交到与展现时相同的URL路径上。也就是说,它会提交到“/spitter/register”上。 

    6.在SpitterController中再添加一个方法来处理这个表单提交,即处理所提交的表单并注册新用户。

     1 package spittr.web;
     2 
     3 import javax.validation.Valid;
     4 
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.stereotype.Controller;
     7 import org.springframework.ui.Model;
     8 import org.springframework.validation.Errors;
     9 import org.springframework.web.bind.annotation.PathVariable;
    10 import org.springframework.web.bind.annotation.RequestMapping;
    11 import org.springframework.web.bind.annotation.RequestMethod;
    12 
    13 import spittr.data.SpitterRepository;
    14 import spittr.spitter.Spitter;
    15 
    16 @Controller
    17 @RequestMapping("/spitter")
    18 public class SpitterController {
    19 
    20     private SpitterRepository spitterRepository;
    21 
    22     public SpitterController() {
    23 
    24     }
    25 
    26     @Autowired // 注入SpitterRepository
    27     public SpitterController(SpitterRepository spitterRepository) {
    28         this.spitterRepository = spitterRepository;
    29     }
    30 
    31     @RequestMapping(value = "/register", method = RequestMethod.GET) // 处理对“/spitter/register”的GET请求
    32     public String showRegistrationForm() {
    33         return "registerForm";
    34     }
    35 
    36     @RequestMapping(value = "/register", method = RequestMethod.POST)
    37     public String processRegistration(@Valid Spitter spitter, // 校验Spitter输入
    38             Errors errors) {
    39         if (errors.hasErrors()) {
    40             return "registerForm"; // 如果校验出现错误,则重新返回表单
    41         }
    42         spitterRepository.save(spitter); //保存Spitter
    43         return "redirect:/spitter/" + spitter.getUsername();  //重定向到基本信息页
    44     }
    45 
    46     @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    47     public String showSpitterProfile(@PathVariable String username, Model model) {
    48         Spitter spitter = spitterRepository.findByUsername(username);
    49         model.addAttribute(spitter);
    50         return "profile";
    51     }
    52 }

    之前创建的showRegistrationForm()方法依然还在,不过新创建的processRegistration()方法,它接受一 个Spitter对象作为参数。这个对象 有firstName、lastName、username和password属性,这些属性将会使用请求中同名的参数进行填充。 

    当使用Spitter对象调用processRegistration()方法时,它会进而调用SpitterRepository的save()方 法,SpitterRepository是在SpitterController的构造器中 注入进来的。 

    processRegistration()方法做的最后一件事就是返回一 个String类型,用来指定视图。但是这个视图格式和以前的视图有所不同。这里不仅返回了视图的名称供视图解析器查找目 标视图,而且返回的值还带有重定向的格式。 如果Spitter.username属性的值为“jbauer”,那么视图将会重 定向到“/spitter/jbauer”。

    需要注意的是,除 了“redirect:”,InternalResourceViewResolver还能识 别“forward:”前缀。当它发现视图格式中以“forward:”作为前缀 时,请求将会前往(forward)指定的URL路径,而不再是重定向。 

    并且在processRegistration()方法中启用校验功能,Spitter参数添加了@Valid注解,这会告知 Spring,需要确保这个对象满足校验限制。 在Spitter属性上添加校验限制并不能阻止表单提交。即便用户没 有填写某个域或者某个域所给定的值超出了最大长 度,processRegistration()方法依然会被调用。这样,我们就 需要处理校验的错误,就像在processRegistration()方法中所 看到的那样。 

    如果有校验出现错误的话,那么这些错误可以通过Errors对象进行 访问,现在这个对象已作为processRegistration()方法的参 数。(很重要一点需要注意,Errors参数要紧跟在带有@Valid注 解的参数后面,@Valid注解所标注的就是要检验的参 数。)processRegistration()方法所做的第一件事就是调 用Errors.hasErrors()来检查是否有错误。

    • 如果有错误的话,Errors.hasErrors()将会返回 到registerForm,也就是注册表单的视图。这能够让用户的浏览 器重新回到注册表单页面,所以他们能够修正错误,然后重新尝试提 交。
    • 如果没有错误的话,Spitter对象将会通过Repository进行保存,控 制器会像之前那样重定向到基本信息页面。

    7.Spitter类,在属性上添加校验注解

     1 package spittr.spitter;
     2 
     3 import javax.validation.constraints.NotNull;
     4 import javax.validation.constraints.Size;
     5 
     6 import org.apache.commons.lang3.builder.EqualsBuilder;
     7 import org.apache.commons.lang3.builder.HashCodeBuilder;
     8 
     9 public class Spitter {
    10 
    11     private Long id;
    12 
    13     @NotNull
    14     @Size(min = 5, max = 16)
    15     private String username;
    16 
    17     @NotNull
    18     @Size(min = 5, max = 25)
    19     private String password;
    20 
    21     @NotNull
    22     @Size(min = 2, max = 30)
    23     private String firstName;
    24 
    25     @NotNull
    26     @Size(min = 2, max = 30)
    27     private String lastName;
    28 
    29     public Spitter() {
    30     }
    31 
    32     public Spitter(String username, String password, String firstName, String lastName) {
    33         this(null, username, password, firstName, lastName);
    34     }
    35 
    36     public Spitter(Long id, String username, String password, String firstName, String lastName) {
    37         this.id = id;
    38         this.username = username;
    39         this.password = password;
    40         this.firstName = firstName;
    41         this.lastName = lastName;
    42     }
    43 
    44     public String getUsername() {
    45         return username;
    46     }
    47 
    48     public void setUsername(String username) {
    49         this.username = username;
    50     }
    51 
    52     public String getPassword() {
    53         return password;
    54     }
    55 
    56     public void setPassword(String password) {
    57         this.password = password;
    58     }
    59 
    60     public Long getId() {
    61         return id;
    62     }
    63 
    64     public void setId(Long id) {
    65         this.id = id;
    66     }
    67 
    68     public String getFirstName() {
    69         return firstName;
    70     }
    71 
    72     public void setFirstName(String firstName) {
    73         this.firstName = firstName;
    74     }
    75 
    76     public String getLastName() {
    77         return lastName;
    78     }
    79 
    80     public void setLastName(String lastName) {
    81         this.lastName = lastName;
    82     }
    83 
    84     @Override
    85     public boolean equals(Object that) {
    86         return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email");
    87     }
    88 
    89     @Override
    90     public int hashCode() {
    91         return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email");
    92     }
    93 
    94 }

    Spitter的所有属性都添加了@NotNull注解,以确保它们的 值不为null。类似地,属性上也添加了@Size注解以限制它们的长 度在最大值和最小值之间。对Spittr应用来说,这意味着用户必须 要填完注册表单,并且值的长度要在给定的范围内。 

    Java校验API定义了多个注解,这些注解可以放到属性上,从而限制 这些属性的值。所有的注解都位于 javax.validation.constraints包中。

       

    8.基本信息展示,profile.jsp 用来展示用户的username和firstName

     1 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
     2 <%@ page session="false" %>
     3 <html>
     4   <head>
     5     <title>Spitter</title>
     6     <link rel="stylesheet" type="text/css" href="<c:url value="/resources/style.css" />" >
     7   </head>
     8   <body>
     9     <h1>Your Profile</h1>
    10     <c:out value="${spitter.username}" /><br/>
    11     <c:out value="${spitter.firstName}" /> <c:out value="${spitter.lastName}" /><br/>
    12   </body>
    13 </html>

    9.测试

    部署到服务器上会发生以下错误,即无法创建JdbcSpitterRepository2这个bean。

    需要在JdbcSpitterRepository2和JdbcSpittleRepository2两个类前增加一个注解,@Service.

    @Service用于标注业务层组件,@Controller用于标注控制层组件

    主页:

    点击Spittles,展示最近发布的20个spittle

    返回点击Register:

    填入个人信息,点击注册:

        

    使用注解的方式进行表单校验时发生错误,一直未解决,现在给出另一种校验方式

    1.spring中自带框架校验器

    (1)spring 校验器接口

    (2)spring 提供的校验类工具,可以提供相应的校验

    2.代码示例

    <1>Spitter.java  没有任何注解

     1 package myspittr.spitter;
     2 
     3 import org.apache.commons.lang3.builder.EqualsBuilder;
     4 import org.apache.commons.lang3.builder.HashCodeBuilder;
     5 
     6 public class Spitter {
     7 
     8     private Long id;
     9 
    10     private String username;
    11 
    12     private String password;
    13 
    14     private String firstName;
    15 
    16     private String lastName;
    17 
    18     public Spitter() {
    19     }
    20 
    21     public Spitter(String username, String password, String firstName, String lastName) {
    22         this(null, username, password, firstName, lastName);
    23     }
    24 
    25     public Spitter(Long id, String username, String password, String firstName, String lastName) {
    26         this.id = id;
    27         this.username = username;
    28         this.password = password;
    29         this.firstName = firstName;
    30         this.lastName = lastName;
    31     }
    32 
    33     public String getUsername() {
    34         return username;
    35     }
    36 
    37     public void setUsername(String username) {
    38         this.username = username;
    39     }
    40 
    41     public String getPassword() {
    42         return password;
    43     }
    44 
    45     public void setPassword(String password) {
    46         this.password = password;
    47     }
    48 
    49     public Long getId() {
    50         return id;
    51     }
    52 
    53     public void setId(Long id) {
    54         this.id = id;
    55     }
    56 
    57     public String getFirstName() {
    58         return firstName;
    59     }
    60 
    61     public void setFirstName(String firstName) {
    62         this.firstName = firstName;
    63     }
    64 
    65     public String getLastName() {
    66         return lastName;
    67     }
    68 
    69     public void setLastName(String lastName) {
    70         this.lastName = lastName;
    71     }
    72 
    73     @Override
    74     public boolean equals(Object that) {
    75         return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email");
    76     }
    77 
    78     @Override
    79     public int hashCode() {
    80         return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email");
    81     }
    82 
    83 }

    <2>自实现校验器SpitterValidator.java类

    rejectIfEmpty函数由三个参数,第一个是返回错误,第二个是绑定的属性名,第三个是返回的错误信息(使用资源文件)。
     1 package myspittr.validates;
     2 
     3 import org.springframework.validation.Errors;
     4 import org.springframework.validation.ValidationUtils;
     5 import org.springframework.validation.Validator;
     6 
     7 import myspittr.spitter.Spitter;
     8 
     9 public class SpitterValidator implements Validator {
    10 
    11     @Override
    12     public boolean supports(Class<?> arg0) {
    13         // TODO Auto-generated method stub
    14         return false;
    15     }
    16 
    17     @Override
    18     public void validate(Object arg0, Errors errors) {
    19         // TODO Auto-generated method stub
    20         Spitter spitter = (Spitter) arg0;
    21 
    22         // 非空校验
    23         ValidationUtils.rejectIfEmpty(errors, "firstName", "spittr.firstName");
    24         ValidationUtils.rejectIfEmpty(errors, "lastName", "spittr.lastName");
    25         ValidationUtils.rejectIfEmpty(errors, "username", "spittr.username");
    26         ValidationUtils.rejectIfEmpty(errors, "password", "spittr.password");
    27     }
    28 
    29 }

    <3>调用  重写SpitterController.java中的processRegistration方法。

     1     @RequestMapping(value = "/register", method = RequestMethod.POST)
     2     public String processRegistration(@Validated Spitter spitter, BindingResult errors) {
     3 
     4         SpitterValidator spitterValidator = new SpitterValidator();
     5         spitterValidator.validate(spitter, errors);
     6         if (errors.hasErrors()) {
     7             return "registerForm2"; // 如果校验出现错误,则重新返回表单
     8         } else {
     9             spitterRepository.save(spitter); // 保存Spitter
    10             return "redirect:/spitter/" + spitter.getUsername(); // 重定向到基本信息页
    11         }
    12 
    13     }

    <4>资源文件

    <5>registerForm.jsp

     1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
     2 <%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf"%>
     3 <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
     4 
     5 <%
     6 String path = request.getContextPath();
     7 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
     8 %>
     9 
    10 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    11 <html>
    12 <head>
    13 <base href="<%=basePath%>">
    14 
    15 <title>注册</title>
    16 
    17 <meta http-equiv="pragma" content="no-cache">
    18 <meta http-equiv="cache-control" content="no-cache">
    19 <meta http-equiv="expires" content="0">
    20 <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    21 <meta http-equiv="description" content="This is my page">
    22 <!--
    23     <link rel="stylesheet" type="text/css" href="styles.css">
    24     -->
    25 
    26 </head>
    27 
    28 <body>
    29 <h1>Register</h1>
    30     <sf:form method="POST" modelAttribute="spitter">
    31             First Name:<sf:input path="firstName" />
    32         <br>
    33         <sf:errors path="firstName">
    34             
    35         </sf:errors>
    36         <br>
    37             
    38             Last Name:<sf:input path="lastName" />
    39         <br>
    40         <sf:errors path="lastName">
    41         
    42         </sf:errors>
    43         <br>
    44             UserName:<sf:input path="username" />
    45         <br>
    46         <sf:errors path="username">
    47             
    48         </sf:errors>
    49         <br>
    50             Password:<sf:password path="password" />
    51         <br>
    52         <sf:errors path="password">
    53             
    54         </sf:errors>
    55         <br>
    56         <input type="submit" value="注册">
    57     </sf:form>
    58 </body>
    59 </html>

    <6>测试

    表单为空时点击注册按钮:

        

    某几个字段为空时:

     

     

     

     

  • 相关阅读:
    多进程交替控制输出
    最长不重复子串
    const关键字的使用
    C++类的内存分布
    shell编程--awk 、sed 命令介绍
    gcc 6.0编译opencv出错
    NTP同步网络时间
    树莓派配置RTC时钟(DS3231,I2C接口)
    浏览器播放rtmp流
    nginx配置hls
  • 原文地址:https://www.cnblogs.com/lyj-gyq/p/8953511.html
Copyright © 2020-2023  润新知