個人博客:Spring MVC+mybatis 項目入門:旅游網(三)用戶注冊 | iwts's blog
先看這個!
這是18年的文章,回收站里恢復的,現階段看基本是沒有參考意義的,技術老舊脫離時代(2024年辣鐵鐵)
如果你在找相關的內容,建議先自我反省一下為什么會搜這么old school的關鍵詞,其次請直接上b站搜索Spricing boo+培訓班,看最新的項目相關視頻
注冊原理
? ? ? ? 其實很簡單,前端頁面顯示一個表單,然后由dispatcher傳遞到controller,controller調用數據庫驗證,如果ok,那就寫入數據庫,同時返回注冊成功的視圖,否則可以返回注冊頁,或者是到一個錯誤頁。
依賴注入與控制反轉
? ? ? ? 這里提一下,在最早接觸servlet的時候,應該有老師會說,Java的POJO應該只有屬性與構造方法,除此之外對于每個屬性必須寫其對應的getter、setter方法。而這里就是為了依賴注入。具體的理論可以百度,這里就簡單說明一下構造注入與setter注入:
// 構造注入
public class Test(){private B b;public Test(B b){this.b = b;}
}// setter注入
public class Test(){private B b;public void setB(B b){this.b = b;}
}
為什么要使用依賴注入或者說控制反轉?(實際上,兩者是相同的,只是在不同的角度闡述了上述操作)這里應該有專門的文章論述了。篇幅有限,這里不再解答,但是推薦搞懂這兩者再繼續閱讀,畢竟這個非常核心。否則就是只會用而不知道具體實現了。
? ? ? ? 現在給出jsp代碼與controller的代碼以及User類的bean:
package me.iwts.bean;import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.Size;public class User {private String account;private String passwd;private String phone;private String email;private String userName;public User(){ }public User(String account,String passwd,String phone,String email,String userName){this.account = account;this.passwd = passwd;this.email = email;this.phone = phone;this.userName = userName;}public void setEmail(String email) {this.email = email;}public void setAccount(String account) {this.account = account;}public void setPasswd(String passwd) {this.passwd = passwd;}public void setPhone(String phone) {this.phone = phone;}public void setUserName(String userName) {this.userName = userName;}public String getEmail() {return email;}public String getAccount() {return account;}public String getPasswd() {return passwd;}public String getPhone() {return phone;}public String getUserName() {return userName;}
}
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><p>注冊測試</p><form:form modelAttribute="user" action="register.action" method="post">賬號:<input type="text" name="account"><br />密碼:<input type="password" name="passwd"><br />手機號:<input type="text" name="phone"><br />郵箱:<input type="email" name="email"><br />用戶昵稱:<input type="text" name="userName"><br /><input type="submit" name="submit" value="注冊"></form:form>
</body>
</html>
這個<form:form>標簽是Spring MVC的標簽,請當做正常的<form>標簽,為什么寫這個標簽?后面的優化部分會說到。
package me.iwts.controller;import me.iwts.bean.User;
import me.iwts.mapper.UserMapper;
import me.iwts.tools.ViewTool;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.Reader;@Controller
public class UserController {// 注冊@RequestMapping("register.action")public ModelAndView register(@ModelAttribute User user, Model model){}
}
具體代碼我沒有寫,這里僅僅演示了Spring MVC如何處理依賴注入的情況。
? ? ? ? 可以看到,form表單對應了User類的一部分,然后就直接action給提交了,并沒有對表單輸入的數據進行封裝。而在controller類里面,我們在形參列表卻傳遞了一個User類。這個User類使用了注解@ModelAttribute。
? ? ? ? 其實這里在Spring MVC,完成了控制反轉或者說依賴注入的操作。我們表單仍然還是只傳遞了一堆值,而dispatcher在獲取請求以后,就利用setter注入,幫助我們封裝好了整個User類,然后是將這個User對象給傳遞到register方法里面的。而只要我們的形參列表聲明了需要這個對象,那么Spring MVC就能夠給我們這個對象。這個過程,就是控制反轉。
? ? ? ? 所以說,從controller的角度看,叫控制反轉,從Spring MVC的角度看,叫依賴注入。而不管怎樣,我們都能夠獲取到這個對象,并且這個對象已經被封裝成為了model,之后的操作就是數據持久化了(當然需要先進行驗證)。
Hibernate Validator后端數據校驗
? ? ? ? 這里也是需要大篇幅講解的部分。。。推薦百度搜一下,提醒一下,這里坑略多,自己搜的時候學得會好一點,就是可能有很多錯,博主也是踩了很多坑。
? ? ? ? Spring是沒有數據校驗的。但是數據校驗是比較重要的一環。可能比較多的同學學的是JavaScript校驗,這個是在前端控制數據的正確性,例如格式問題。但是,如果某些不懷好意的同學惡意操作呢?例如直接使用http提交數據,這樣就能繞過前端直接給后端傳遞數據。當然安全問題遠遠沒有這么低級,但是在現階段,這樣的問題應該被我們考慮,而更難的安全問題就以后在進行處理。所以,解決這個簡單的安全問題就是進行后端的數據校驗——無論你怎么傳,只要我能在后端校驗,就能防止惡意傳遞數據。
? ? ? ? 比較簡單的方法就是直接處理——我們已經將對象封裝好利用控制反轉給獲取到了,那么我們就能獲取其各個屬性的值,然后直接一頓操作就行了。但是我們現在想要逼格高一點的,同時還想節省代碼量,所以我們選擇利用其它技術來實現這個功能。
? ? ? ? 上面也說了Spring是沒有數據校驗的。簡而言之,Java只提供了一些規范,說,只要你能實現這個規范,就能進行數據校驗了,而hibernate validator就是實現了這個規范。那么我們就只用獲取其jar包,然后一頓調用,就能利用其來實現數據校驗的操作。hibernate validator是實現了兩套規范的,我們下面講的主要依據最新的規范,比較簡單,也更強大。
也可以移步:Spring MVC利用Hibernate Validator實現后端數據校驗 | Iwts’s blog?有更為詳盡的解釋
? ? ? ? 首先,jar包自然是需要的。hibernate validator所必須的jar包是2個:hibernate-validator.jar和validation-api.jar。但是個人推薦多增加兩個,可以杜絕大部分錯誤:
但是如果還是有錯的話,就只能看log了,具體缺什么jar包就去下載什么jar包。這些jar包都可以直接百度下載,或者在我的項目里面/lib/ext下查找。而具體怎么在IDE里面添加就不多說了。
約束注解
? ? ? ? 之后,我們需要對bean進行一次升級,就是添加注解。而這個注解,就是對某個屬性進行約束,規定這個屬性必須滿足怎么樣的條件,否則就會返回錯誤。先看一下bean的代碼:
package me.iwts.bean;import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.Size;public class User {@Size(min = 6,max = 16,message = "賬號不能為空,位數要為6-16位")private String account;@Size(min = 6,max = 16,message = "密碼不能為空,位數要為6-14位")private String passwd;@Size(min = 11,max = 11,message = "手機不能為空,手機號碼格式錯誤")private String phone;@Email(message = "郵箱格式錯誤")private String email;@Size(min = 0,max = 10,message = "昵稱不能大于10位")private String userName;public User(){ }public User(String account,String passwd,String phone,String email,String userName){this.account = account;this.passwd = passwd;this.email = email;this.phone = phone;this.userName = userName;}public void setEmail(String email) {this.email = email;}public void setAccount(String account) {this.account = account;}public void setPasswd(String passwd) {this.passwd = passwd;}public void setPhone(String phone) {this.phone = phone;}public void setUserName(String userName) {this.userName = userName;}public String getEmail() {return email;}public String getAccount() {return account;}public String getPasswd() {return passwd;}public String getPhone() {return phone;}public String getUserName() {return userName;}
}
可以看到,每個屬性上面對應的@Size、@Email等就是注解。不同的注解有不同的作用,這里提供一些圖,是從以前的博客上截的:
利用注解,就能比較方便地進行約束。
? ? ? ? 現在只是聲明了約束,而如果違反這個約束會有什么操作這個是在controller里面執行的,但是我們需要告訴controller這里違反了約束,也就是需要提醒信息。可以看到,我們在注解里面寫了message屬性,而里面的內容就是我們自定義的錯誤信息。其實不加也行,如果我們不想讓用戶看到這個信息的話,默認情況下也會有錯誤信息,不過是英文的。但是我們選擇讓用戶看到,這樣能提醒他們你寫錯了。怎么讓他們看?這個我們留到最后說。
后端處理
? ? ? ? 那么現在,就看我們怎么在controller里面處理這個約束了,看一下controller里面的代碼:
package me.iwts.controller;import me.iwts.bean.User;
import me.iwts.mapper.UserMapper;
import me.iwts.tools.ViewTool;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.Reader;@Controller
public class UserController {// 注冊@RequestMapping("register.action")public ModelAndView register(@Valid @ModelAttribute User user, BindingResult bindingResult, Model model){if(bindingResult.hasErrors()){model.addAttribute("user",user);return new ModelAndView(ViewTool.REGISTER);}}
}
可以與上面代碼進行一些比較,其實主要是參數部分有變化:
1.對于傳入的對象,需要用注解@Valid聲明。
2.增加一個BindingResult對象。
第一個操作,主要是聲明,在進行依賴注入的時候,需要對這個類的屬性進行數據驗證,而驗證方式就是根據其對應的注解。而BindingResult對象,就是在進行數據驗證的時候,如果有錯誤,就將其message給添加到BindingResult對象里面。而調用其hasErrors()方法,就能判定是否是有錯誤的。
? ? ? ? 而具體如何處理,這個就根據實際情況判定了。例如我們對于無所謂的數據,例如用戶昵稱。我們允許用戶不寫昵稱,但是我們看論壇的話,發現這個昵稱會默認是用戶名。這就是我們處理的結果了,如果發現有用戶昵稱為空,我們就將用戶名給賦值進去。
? ? ? ? 當然,我們這里的邏輯就是告訴用戶:你錯了,請重新輸入。所以可以看到,我們直接返回了一個視圖,同時將user對象封裝進model里面,和視圖一起返回到注冊頁面,所以下面就是看前端如何處理了。
前端處理
? ? ? ? 現在,我們將model返回到了前端,同時視圖也返回回來了,這里先上一個完整未刪減的代碼:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><p>注冊測試</p><form:form modelAttribute="user" action="register.action" method="post">賬號:<input type="text" name="account" value=${requestScope.user.account}><form:errors path="account"></form:errors><span>${accountError}</span><br />密碼:<input type="password" name="passwd"><form:errors path="passwd"></form:errors><br />手機號:<input type="text" name="phone" value=${requestScope.user.phone}><form:errors path="phone"></form:errors><br />郵箱:<input type="email" name="email" value="${requestScope.user.email}"><form:errors path="email"></form:errors><br />用戶昵稱:<input type="text" name="userName" value=${requestScope.user.userName}><form:errors path="userName"></form:errors><br /><input type="submit" name="submit" value="注冊"></form:form>
</body>
</html>
同樣,可以跟最早的jsp頁面比較,看多了點什么東西。
? ? ? ? 首先,類似于${requestScope.user.account}這樣的代碼是EL表達式。這個是非常好用的,推薦大家先去看一下什么是EL表達式,然后再回頭看這里的代碼。只能說,用EL表達式很爽。
? ? ? ? 然后,下面就默認大家會一點EL表達式了。首先可以看到,多的一部分是value值。這個部分是完成了記憶功能。例如剛開始注冊表單是什么都沒有的,而我們注冊以后,如果有錯誤返回,會發現表單是我們上次提交的信息,除了密碼。這里就是利用value進行記憶功能,value的值就是EL表達式,而剛開始EL表達式是找不到user對象的,因為我們只有在model里面將user返回,才有這個對象,所以EL表達式的結果是空。而如果第二次返回,那么就有user對象了,從而能夠將上次輸入的結果給顯示在界面上。
? ? ? ? 這個不是最重要的,重要的是下面的標簽:<form:errors>,這個標簽能夠顯示hibernate validator捕獲的錯誤數據。并且將其message給顯示出來。而path屬性就指定了,其顯示哪一個屬性出現的錯誤。請注意:想要使用這個標簽,那么就必須使用<form:form>標簽,這也是我放棄<form>標簽的原因。
? ? ? ? 所以,如果你想要讓用戶看到哪里錯了,就需要在message屬性寫想讓用戶看到的信息,如果不想,就可以使用默認message了。給一個效果圖吧:
下一章鏈接
https://blog.csdn.net/iwts_24/article/details/84198196