笔记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>測試
表單為空時點擊注冊按鈕:
? ??
某幾個字段為空時:
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/lyj-gyq/p/8953511.html
總結
以上是生活随笔為你收集整理的笔记28 接受请求的输入 ——处理表单的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么看待腾讯元宝对就业市场的影响?
- 下一篇: 为啥腾讯元宝要进行风险评估?