SpringBoot其實不是新框架,而是默認配置了很多框架的使用方式。就像maven整合了所有jar包,Springboot整合了所有框架,並通過一行簡單的main方法啟動應用。
繼承了spring的框架們:
電商秒殺應用簡介:
商品列表頁獲取秒殺商品列表
進入商品詳情頁獲取秒殺商品詳情
秒殺開始后進入下單確認頁下單並支付成功
項目實戰:
使用IDEA+Maven搭建SpringBoot開發環境
集成Mybatis操作數據庫
實現秒殺項目
第2章·基礎項目搭建的基本流程:
構建Maven項目—>引入SpringBoot依賴—>接入Mybatis
2.1 使用IDEA創建maven項目
new->project->maven項目->選擇maven-archetype-quickstart
1.導入springboot相關配置:
https://blog.csdn.net/m0_37657841/article/details/90524410
https://blog.csdn.net/midnight_time/article/details/90717676
跟着做完后輸入8080出現了一下頁面說明springboot工程在不需要任何外力下內嵌了tomcat容器
2.SpringMVC+RESTful的使用:
???有點失敗,網頁沒有出來,依舊是上面那樣。沒有報錯也不知為啥。
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
----------------------------------------------------------------
看到報錯了。。。8080已經被用了??之前tomcat不是都關了嗎。。
注:8080 是默認端口,如果要使用其他端口,可以在res下面寫 application.properties 修改,如:server.port=8090
然后我把idea停止運行再試就成功了。。。。所以是自己占了自己??暈
好了,還是挺神奇的,都沒有在index.jsp上面寫對應到方法的requestmapping?(不過簡單的顯示確實不需要頁面。。。
3.接入Mybatis(看安利視頻的博主圖文並茂的博客鏈接)
mybatis竟然有自動生成mapping.xml文件的插件。。。
創建數據庫,填寫mybatis-generator.xml,這個xml里面指定mybatis插件連接數據庫和讓插件生成什么文件生到哪里。
運行Mybatis插件,自動生成相關文件
就還挺神奇的,實體類(對應表),DAO接口(寫方法)和mapping.xml文件(sql語句),這3個都不需要手動編寫了。
只需要把包寫好就行,比如實體類老師先寫好了包dataobject,然后改生成文件中的包的路徑com.miaoshaproject.dataobject
然后寫生成對應表及類名
運行run:mybatis_generator
然后application.properties中配置SpringBoot項目數據源
------------------------------------------------------------------------------------------------------------------
編寫測試的時候@MapperScan注解報紅??
找半天后發現pom文件里mybatis的包引錯了。。。
要用mybatis-springboot
<!--5、引入的第五個:Mybatis相關的jar包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
------------------------------------------------------------------------------------------------------
然后要運行app這個,換過來。千萬運行之前那個generator的maven
網友總結了所有代碼,但沒有詳細注解說明:
https://www.cnblogs.com/victorbu/p/10538615.html
輸入8090端口可以看到了:
然后手動網數據庫插入一條數據:
顯示數據庫中找到的用戶的名字
以上就是第二章基礎搭建,springboot接入mybatis,編寫generator.xml讓插件自動生成實體類,dao接口和映射文件。
第三章 用戶模塊開發
上一章,我們學會了:
構建Maven項目
引入SpringBoot
引入Mybatis
簡單使用SpringMVC
https://blog.csdn.net/midnight_time/article/details/91048543
https://blog.csdn.net/m0_37657841/article/details/90545984
接下來就要詳細的學習使用SpringMVC了,具體知識點有:
1.MVC分層架構
之前學的都是數據庫查詢到的一行實體對象數據直接返回到前端,但實際開發中不可以直接返回數據庫的數據,需要service層有一個model掩蓋一下。
password屬性也加到model里面。 model層才是真正處理業務核心層,而dataobject實體類僅僅只是對數據庫的映射。
但是model也不需要全都返回給前端,所以在cotroller層再增加一個viewobject層。
由cotroller層的@requestbody和@requestparm在頁面展示了json數據
1. DAO層 --- dataobject,與數據庫一一映射
2. Service層 --- model,整合因業務需求而分離的dataobject
3. Controller層 --- viewobject,屏蔽如密碼等敏感信息返回給前端
2.通用返回類型
Controller中的方法返回值類型會出現不同,比如返回viewobject,返回null,返回異常Exception信息等等。如果每個方法都有獨自的返回值類型的話,那樣的設計不太好,也不容易以統一的形式(比如統一的JSON格式)呈現給前端。好的解決方法就是獨立出一層:response,創建一種通用的返回值類型CommonReturnType,它有兩個屬性status和data。
1.增加一個response包。創建CommonReturnType類給他統一返回類型。

public class CommonReturnType { //表明對應請求的返回結果 success fail private String status; //若status=success,則data內返回前端需要的json數據 //若status=fail,則data內使用通用的錯誤碼格式 private Object data; // public CommonReturnType(String status, Object data) {//這才是構造方法 // this.status = status; // this.data = data; // } //定義一個通用的創建方法 是自己寫的方法 不是構造方法 那為什么不用構造方法呢?因為他這個可以返回值! public static CommonReturnType create(Object result){//傳入對象或基本數據類型都可以 return create(result, "success");//調用下面這個重載的方法,傳入的都依然返回 但是多設置了一個status } public static CommonReturnType create(Object result, String status){ CommonReturnType type = new CommonReturnType(); type.setStatus(status); type.setData(result); return type; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
修改cotroller的返回值為這個,則可以看到瀏覽器返回多了個status和data 統一的返回類型
(學MVC時那個前端控制器和視圖控制器可以自動對應success.jsp,fail.jsp也很好用。。這里springboot好像沒有web.xml配置這些了??
定義通用的返回對象——返回錯誤信息
1.創建error包
2.創建commonError接口
public interface CommonError { public int getErrCode(); public String getErrMsg(); public CommonError setErrMsg(String errMsg);
3.創建接口的實現類為枚舉類(就可以直接寫定義),因為異常有ErrorCode和ErrorMsg兩個屬性,因此我們將異常封裝一下抽象到一個枚舉類EmBusinessError中

//實現類是一個枚舉類 這個類可以傳給cotroller里面捕獲異常相關的繼承exception的新建一個BusinessException類 public enum EmBusinessError implements CommonError { //通用錯誤類型00001 PARAMETER_VALIDATION_ERROR(00001, "參數不合法"), //10000開頭為用戶信息相關錯誤定義 USER_NOT_EXIST(10001, "用戶不存在") //下面還可以繼續方便增加枚舉的錯誤信息 代碼修改可用性高 ; private int errCode; private String errMsg; EmBusinessError(int errCode, String errMsg) { this.errCode = errCode; this.errMsg = errMsg; } @Override public int getErrCode() { return this.errCode; } @Override public String getErrMsg() { return this.errMsg; } @Override public CommonError setErrMsg(String errMsg) { this.errMsg = errMsg; return this; } }
使用這個類就是,這個類可以傳給cotroller里面捕獲異常相關的繼承exception的新建一個BusinessException類

//包裝器業務異常實現 和em類一樣實現接口,且都可以通過set方法覆蓋掉原來的接口對象 public class BusinessException extends Exception implements CommonError { private CommonError commonError; //直接接收EmBusinessError的傳參用於構造業務異常(?怎么傳啊?這個參數就是來自em實現類?還是里面的set方法返回值? // 應該是后者em的set方法返回值傳進這里面初始化) public BusinessException(CommonError commonError) { super();//有一些Exception自身初始化的機制在里面 this.commonError = commonError; } //接收自定義errMsg的方式構造業務異常 public BusinessException(CommonError commonError, String errMsg) { super(); this.commonError = commonError; this.commonError.setErrMsg(errMsg); } @Override public int getErrCode() { return this.commonError.getErrCode(); } @Override public String getErrMsg() { return this.commonError.getErrMsg(); } @Override public CommonError setErrMsg(String errMsg) { this.commonError.setErrMsg(errMsg); return this; } }
然后就可以在controller里面拋出了,用法是這樣的,參數直接傳寫好的枚舉就可以!!省事兒!
//若獲取的對應用戶信息不存在 if (userModel == null) { throw new BusinessException(EmBusinessError.USER_NOT_EXIST); }
但不管是直接throw的業務異常或者JVM虛擬機拋出的運行時異常,僅僅是拋到了Tomcat的容器層,我們在Controller層並不能進行處理。所以接着來
4.定義@exceptionHandler解決未被controller層吸收的exception
像處理異常這種工作,不只是UserController要用到,以后處理其他模塊也有可能有異常,因此將代碼提取到新建一個BaseController中,讓UserController繼承BaseController,這樣以后擴展其他模塊時,代碼就可以復用了。

@Controller("user") @RequestMapping("/user") public class UserController extends BaseController{ @Autowired private UserService userService; @RequestMapping("/get") @ResponseBody public CommonReturnType getUser(@RequestParam(name = "id") Integer id) throws BusinessException { //調用service服務獲取對應id的用戶對象並返回給前端 UserModel userModel = userService.getUserById(id); //若獲取的對應用戶信息不存在 if (userModel == null) { // userModel.setEncrptPassword("123"); throw new BusinessException(EmBusinessError.USER_NOT_EXIST); } //將核心領域模型用戶對象轉化為可供UI使用的viewobject UserVO userVO = convertFromModel(userModel); //返回通用對象 return CommonReturnType.create(userVO); } //把model轉成VO 主要是為了調用service方法生成的是model,但我們需要VO類 private UserVO convertFromModel(UserModel userModel){ if(userModel == null){ return null; } UserVO userVO = new UserVO(); BeanUtils.copyProperties(userModel,userVO); return userVO; } }
以上統一了返回類型和錯誤信息
完成了基礎能力建設,接下來進入模型能力管理。功能方面了
用戶信息管理:
otp短信獲取
otp注冊用戶
用戶手機登錄
3.otp(一次性密碼)驗證碼生成
這里是后台生成隨機數發短信給用戶,跟之前學的網頁上生成隨機數讓用戶填寫不太一樣
//用戶獲取!!otp短信方法 @RequestMapping("/getotp") @ResponseBody public CommonReturnType getOtp(@RequestParam(name="telphone")String telphone){ //需要按照一定的規則生成OTP驗證碼 就是單純的Random隨機數 Random random = new Random(); int randomInt = random.nextInt(99999);//此時隨機數取值[0,99999) randomInt+=10000;//此時取值[10000,109999) String otpCode = String.valueOf(randomInt); //將OTP驗證碼同對應用戶的手機號關聯 企業級是使用Redis來實現的,(鍵值對形式的數據庫,反復點擊可以反復覆蓋) // 但是目前這個項目屬於入門級別沒講到分布式,暫時用httpServletRequest對象獲取session方式來綁定手機號與驗證碼。 request.getSession().setAttribute(telphone,otpCode);//就這樣做關聯了 //將OTP驗證碼通過短信通道發送給用戶,省略(有興趣可買第三方服務的短信通道) System.out.println("telphone = "+telphone+"&otpCode ="+otpCode); return CommonReturnType.create(null); }
控制台打印了手機號和綁定的驗證碼:
3.6 用戶模型管理——Metronic模板簡介
采用前后端分離的思想,建立一個html文件夾,引入static文件夾(下載資料里)
前端文件保存在本地的哪個盤下都可以,因為是通過ajax來異步獲取接口
getotp.html:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>GetOtp</title> <script src = "static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script> <link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/> <link href="static/assets/global/plugins/css/component.css" rel="stylesheet" type="text/css"/> <link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/> </head> <body class="login"> <div class="content"> <h3 class="form-title">獲取otp信息</h3> <div class="form-group"> <label class="control-label">手機號</label> <div> <input class="form-control" type="text" placeholder="手機號" name="telphone" id="telphone"/> </div> </div> <div class="form-actions"> <button class="btn blue" id="getotp" type="submit"> 獲取otp短信 </button> </div> </div> </body> <script> jQuery(document).ready(function () { //綁定otp的click事件用於向后端發送獲取手機驗證碼的請求 $("#getotp").on("click",function () { var telphone=$("#telphone").val(); if (telphone==null || telphone=="") { alert("手機號不能為空"); return false; } //映射到后端@RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) $.ajax({ type:"POST", contentType:"application/x-www-form-urlencoded", url:"http://localhost:8090/user/getotp", data:{ "telphone":$("#telphone").val(), }, success:function (data) { if (data.status=="success") { alert("otp已經發送到了您的手機,請注意查收"); }else { alert("otp發送失敗,原因為" + data.data.errMsg); } }, error:function (data) { alert("otp發送失敗,原因為"+data.responseText); } }); }); }); </script> </html>
這里的$#是取地址去id取元素 好像不是jsp的el表達式
但是有跨域請求出錯,因為html是本地文件的端口,服務器是主機端口。跨域請求錯誤,只需要在UserController類上加一個注解@CrossOrigin
即可
控制台打印成功
4.登錄、注冊功能
3.9 用戶模型管理——用戶注冊功能實現
1.實現方法:conttoller中用戶注冊方法,也是通用返回類型給前端,就是數據加狀態,不返回就create方法傳進null。
// 用戶注冊方法 @RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) @ResponseBody //要從請求體拿的參數就都要寫出 public CommonReturnType register(@RequestParam(name = "telphone") String telphone, @RequestParam(name = "otpCode") String otpCode, @RequestParam(name = "name") String name, @RequestParam(name = "gender") String gender, @RequestParam(name = "age") String age, @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException { //驗證手機號和對應的otpCode相符合 String inSessionOtpCode = (String) this.request.getSession().getAttribute(telphone);//找到session里的otp(用戶提交的手機號必須一致才能找到) if (!com.alibaba.druid.util.StringUtils.equals(otpCode, inSessionOtpCode)) {throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信驗證碼不符合"); }//符合的話往下走 給數據庫傳入用戶前端填寫的數據 所以要去業務層寫方法 //用戶的注冊流程 UserModel userModel = new UserModel(); userModel.setName(name); userModel.setAge(Integer.valueOf(age)); userModel.setGender(Byte.valueOf(gender)); userModel.setTelphone(telphone); userModel.setRegisterMode("byphone"); //密碼加密才能傳入數據庫(?。。 userModel.setEncrptPassword(this.EncodeByMd5(password)); userService.register(userModel); return CommonReturnType.create(null); } //密碼加密 public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException { //確定計算方法 MessageDigest md5 = MessageDigest.getInstance("MD5"); BASE64Encoder base64en = new BASE64Encoder(); //加密字符串 String newstr = base64en.encode(md5.digest(str.getBytes("utf-8"))); return newstr; }
要做校驗在傳入數據庫之前判斷各個屬性的數據是否為空,在serviceimpl里面加注冊register方法。
@Override @Transactional public void register(UserModel userModel) throws BusinessException { if (userModel == null) { throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); } if (StringUtils.isEmpty(userModel.getName()) //因為字符串類型不能用==null判斷所以用StringUtils.isEmpty方法(依賴的?) || userModel.getGender() == null || userModel.getAge() == null || StringUtils.isEmpty(userModel.getTelphone())) { throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); } //實現傳入的model->dataobject方法 下面寫一個轉換方法 這些框架以后也有用 UserDO userDO = convertFromModel(userModel); userDOMapper.insertSelective(userDO);//這個好像之前也學過我還測試來着(學框架的時候在哪里???找不着了。。。) // 就是如果傳入了null是不會覆蓋之前有數據的值。如果是insert就會覆蓋了。 UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel); userPasswordDOMapper.insertSelective(userPasswordDO); return; } //model->dataobject的轉換方法 private UserDO convertFromModel(UserModel userModel) { if (userModel == null) { return null; } UserDO userDO = new UserDO(); BeanUtils.copyProperties(userModel, userDO); return userDO; } //model的password->dataobject的password轉換方法 private UserPasswordDO convertPasswordFromModel(UserModel userModel) { if (userModel == null) { return null; } UserPasswordDO userPasswordDO = new UserPasswordDO(); userPasswordDO.setEncrptPassword(userModel.getEncrptPassword()); userPasswordDO.setUserId(userModel.getId()); return userPasswordDO; }
引入做輸入校驗的依賴(apache.lang包) StringUtils用的這個包
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency>
提交到數據庫insert要記得加事務控制。@Transactional
首先在getotp界面添加注冊成功的跳轉界面,然后寫前端注冊界面:也用h5 復制getotp寫一個register.html
七、用戶登錄流程
還需要解決跨域的問題,因為
//跨域請求中,不能做到session共享,要設置
@CrossOrigin(allowCredentials = "true",allowedHeaders = "*")
然后前端也要加ajax受信
//允許跨域請求
xhrFields:{withCredentials:true},
--------------------------------------------------------------------------------------------------
我加了register.html忘記加getotp了 debug半天也不太會,最后感覺sout比debug好使。。。
-------------------------------------------------------------------------------------------------
還是出錯,剛剛是手機號和驗證碼不匹配的錯誤,現在直接未知錯誤。。。。
debug半天發現是serviceimpl的這里有問題 那么就是mapper的問題??這不是自動生成的嘛。。。。我暈
還有我都給model設置id了它還是等於null???可能idea已經被我這頻繁啟動暫停折磨瘋掉了...
這都什么人間疾苦。。
包導錯了改過來了(StringUtils這個包用剛剛添加的依賴apache公司的),還是不成功.................................
花費一下午也沒找出問題,放棄了。總之這個包是得注釋掉,但是注釋掉這個包,我的數據庫操作還是可能有問題。不再用這個了。
ps:github下載的要重新設置maven的倉庫地址,setting地址和證書-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true
然后clean-install-reimport操作。但首先要改成本地都有的版本比較好。
------------------------------------------------------------------------------------------------------------
<insert id="insertSelective" parameterType="com.miaoshaproject.dataobject.UserDO" keyProperty="id" useGeneratedKeys="true">
添加id自增
------------0529經同學找錯發現錯在數據庫中表的設計,非空必須要有默認值--------------
varchar類型默認空的就是,寫兩個單引號 ‘’
int類型默認0就是
id有自增所以不必默認
改過來真的就注冊成功了.....難怪必須用下載的數據庫,人家的建表語句里定義了默認值...
還有就是,返回未知錯誤沒有返回什么錯誤,找錯誤應該想到所有異常最后都匯聚的BaseController里面
增加打印錯誤
----------------------------------------------------------------------------------------------------------------
上面並沒有做手機號的唯一性驗證
首先,在數據庫中添加索引:
索引名稱為:telphone_unique_index,索引字段選擇telphone,索引類型為UNIQUE,索引方法為BTREE
然后修改以下代碼:
try { userMapper.insertSelective(userDO); } catch (DuplicateKeyException ex) { throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "手機號已注冊");//二重奏構造之二自定義的業務錯誤 }
八、用戶登錄流程
用戶注冊流程,后端Controller層需要獲取用戶輸入,然后封裝成Model,傳遞給Service層,Service層再拆分成dataobject,調用DAO層,將DO插入數據庫。
登錄和注冊流程相似,只不過在調用DAO層時,不是進行插入操作,而是查詢操作。
1.controller層寫登錄方法/login

//用戶登錄接口 @RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) @ResponseBody public CommonReturnType login(@RequestParam(name = "telphone") String telphone, @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException { //入參校驗 if (StringUtils.isEmpty(telphone) || StringUtils.isEmpty(password)) { throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); } //用戶登錄服務,用來校驗用戶登錄是否合法 //用戶加密后的密碼 UserModel userModel = userService.validateLogin(telphone, this.EncodeByMd5(password)); //將登陸憑證加入到用戶登錄成功的session內 this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true); this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel); return CommonReturnType.create(null); }
2.service層
接口
/* telphone:用戶注冊手機 encrptPassowrd:用戶加密后的密碼 */ UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException;
實現類
@Override public UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException { //通過用戶手機獲取用戶信息 UserDO userDO = userDOMapper.selectByTelphone(telphone); if (userDO == null) { throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL); } userPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId()); UserModel userModel = convertFromDataObject(userDO, userPasswordDO); //比對用戶信息內加密的密碼是否和傳輸進來的密碼相匹配 if (StringUtils.equals(encrptPassword, userModel.getEncrptPassword())) { throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL); } return userModel; }
返回業務異常時用到的是在枚舉類里直接拿的
所以要在枚舉類里添加該異常
USER_LOGIN_FAIL(20002,"用戶手機號或密碼不正確"),
3.dao
需要添加四個文件(Mybatis自動生成的只有selectByPrimaryKey)
UserDOMapper 添加 selectByTelphone
UserPasswordDOMapper 添加 selectByUserId (這個之前測試返回類型的時候寫過了!!
DOMapper.java中的方法與DOMapper.xml 中數據庫CURD語句一一映射。
4.前端login.html
<body class="login"> <div class="content"> <h3 class="form-title">用戶登錄</h3> <div class="form-group"> <label class="control-label">手機號</label> <div> <input class="form-control" type="text" placeholder="手機號" name="telphone" id="telphone"/> </div> </div> <div class="form-group"> <label class="control-label">密碼</label> <div> <input class="form-control" type="password" placeholder="密碼" name="password" id="password"/> </div> </div> <div class="form-actions"> <button class="btn blue" id="login" type="submit"> 登錄 </button> <button class="btn green" id="register" type="submit"> 注冊 </button> </div> </div> </body> <script> jQuery(document).ready(function () { //綁定注冊按鈕的click事件用於跳轉到注冊頁面 $("#register").on("click",function () { window.location.href = "getotp.html"; }); //綁定登錄按鈕的click事件用於登錄 $("#login").on("click",function () { var telphone=$("#telphone").val(); var password=$("#password").val(); if (telphone==null || telphone=="") { alert("手機號不能為空"); return false; } if (password==null || password=="") { alert("密碼不能為空"); return false; } //映射到后端@RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) $.ajax({ type:"POST", contentType:"application/x-www-form-urlencoded", url:"http://localhost:8090/user/login", data:{ "telphone":telphone, "password":password }, //允許跨域請求 xhrFields:{withCredentials:true}, success:function (data) { if (data.status=="success") { alert("登錄成功"); }else { alert("登錄失敗,原因為" + data.data.errMsg); } }, error:function (data) { alert("登錄失敗,原因為"+data.responseText); } }); return false; }); });
有點好奇客戶端非webview展示html的,那種原生的app怎么傳數據給服務器。尤其我的課題不能用表單傳,應該也是ajax這樣data里面就可以
點擊登錄:
點擊注冊回到getotp.html
3.10 優化校驗規則
去倉庫可以直接搜有用的輪子 ,老師直接搜了validator然后點擊版本號找依賴就是。
<!--校驗--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version> </dependency>
好用❤❤❤❤❤
好用❤❤❤❤
好用❤❤❤❤
http://maven.outofmemory.cn/hot/
2.對validator進行一個簡單的封裝
新建validator的目錄
新建一個ValidationResult的類
校驗后的結果類,有一個布爾類型的hasError屬性,一個Map類型的errorMsgMap,和他們的set,get方法。
還有一個getErrMsg的方法從Map拿value。
public class ValidationResult { //校驗結果是否有錯 private boolean hasErrors = false; //存放錯誤信息的map private Map<String, String> errorMsgMap = new HashMap<>(); public boolean isHasErrors() { return hasErrors; } public void setHasErrors(boolean hasErrors) { this.hasErrors = hasErrors; } public Map<String, String> getErrorMsgMap() { return errorMsgMap; } public void setErrorMsgMap(Map<String, String> errorMsgMap) { this.errorMsgMap = errorMsgMap; } //實現通用的通過格式化字符串信息獲取錯誤結果的msg方法 public String getErrMsg() { return StringUtils.join(errorMsgMap.values().toArray(), ",");
//join方法是在每個元素之間打后面的參數即逗號,萬一又是年齡不為空,手機號不為空等多個錯誤 } }
新建一個ValidatiorImpl的類
主要是實現implements InitializingBean接口的實現類,配合實體類上的屬性們寫注解用
@Component public class ValidatorImpl implements InitializingBean { private Validator validator;//校驗包里的類 //實現校驗方法並返回校驗結果 public ValidationResult validate(Object bean) {//主要是這個方法做校驗!!! final ValidationResult result = new ValidationResult(); Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean); if (constraintViolationSet.size() > 0) { //有錯誤 result.setHasErrors(true);//此時hasError屬性為true了 constraintViolationSet.forEach(constraintViolation ->{ String errMsg = constraintViolation.getMessage();//在實體類屬性的注解上 String propertyName = constraintViolation.getPropertyPath().toString(); result.getErrorMsgMap().put(propertyName, errMsg);//校驗結果類存入錯誤信息 }); } return result; } @Override public void afterPropertiesSet() throws Exception { //將hibernate validator通過工廠的初始化方式使其實例化 this.validator = Validation.buildDefaultValidatorFactory().getValidator(); } }
在UserServiceImpl中使用validator做校驗,之后要給別的實體類做校驗也可以用
//校驗入參 ValidationResult result = validator.validate(userModel); if (result.isHasErrors()) { throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
//后面是錯誤的信息,實體類注解上的,會覆蓋 }
使用這個校驗在業務層impl,以后做校驗時只需要在model的屬性上做注解即可。
給屬性們加注解作為限制條件@NotNull @NotBlank
要認准這個包!!!
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class UserModel {
private Integer id; @NotBlank(message = "姓名不能為空") private String name; @NotNull(message = "姓名不能為空") private Byte gender; @NotNull(message = "年齡不能為空") @Min(value = 0,message = "年齡不能小於0")//還能加這種判斷條件 @Max(value = 150,message = "年齡不能大於150") private Integer age; @NotBlank(message = "手機號不能為空") private String telphone; private String registerMode; private String thirdPartyId; @NotBlank(message = "密碼不能為空") private String encrptPassword;
我們的報錯系統和校驗系統就很健壯了。
----------------------------------------------------------------------------------------------------------------------------------------------
關於后端所有帶異常的方法的返回值
我們這套代碼是前后端分離,且前后端都有進行校驗。有些校驗只有后端才能完成,
比如說:
手機號是否重復
驗證碼是否獲取成功
是否登錄成功
是否注冊成功
參數是否合法
用戶登錄手機號和密碼是否匹配等等。
這些校驗之后,如果異常則會拋出對應的異常, Service層throw Exception會拋到調用服務的Controller層,Controller中throw Exception最終都會在BaseController中進行return給前端。(走的還是通用返回類型)
列一下后端所有帶異常的方法的返回值,有助於邏輯的理解。前面學的時候沒太明白。
正確信息通用返回CommonReturnTyppe使用二重奏方法,只傳入data或者null就是方法一默認的狀態是success,方法二傳入data和自定義string類型的狀態。
異常信息最終也是在basecontroller里用通用返回類型方法二,responsedata是一個map集合接收businessexception里的異常信息作為data,后面是自定義string的狀態"fail"。
具體說這個responedata是commonerror接口的實現類bussinessexception繼承exception獲得的,他的構造方法是二重奏方法,第一個構造方法可以直接傳入也是commonerror接口實現類的embusinesserror枚舉設置好的錯誤類型,第二個構造方法是傳入coomonerror還傳入自定義的errmsg覆蓋掉commonerror里面的errmsg.
{ "status":"fail", "data":{ "errorCode":10001, "errorMsg":"未知錯誤"//來自枚舉類的UNKNOWN_ERROR(10002, "未知錯誤"),但是是由baseController里面的定義系統錯誤時賦上的名字
} }
|
類名 | 方法名 | 返回正確信息 | 返回異常信息 |
Controller層 | BaseController | handlerException | 無 | return CommonReturnType.create (responseData,“fail”);//所以最終還是通用返回類型 |
Controller層 | UserController | getUser | return CommonReturnType .create(userVO); |
無,但會throw new BusinessException (EmBusinessError.USER_NOT_EXIST); 通過BaseController進行return |
Controller層 | UserController | getOtp | return CommonReturnType .create (otpCodeObj, “successGetOtpCode”); |
無,但會throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR, “手機號已重復注冊”); 通過BaseController進行return |
Controller層 | UserController | register | return CommonReturnType. create(null); |
無,但會 throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR, “短信驗證碼錯誤”); 通過BaseController進行return |
Controller層 | UserController | login | return CommonReturnType. create(null); |
無,但會throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR); 通過BaseController進行return |
Service層 | UserServiceImpl | register | 無 | throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR); 通過BaseController進行return throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg()); 通過BaseController進行return throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR, “手機號已重復注冊”); 通過BaseController進行return |
Service層 | UserServiceImpl | validateLogin | return userModel; | throw new BusinessException (EmBusinessError.USER_LOGIN_FAIL); 通過BaseController進行return throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR); 通過BaseController進行return |
----------------------------------------------------------------------------------------------
總結:
第二章 應用SpringBoot完成基礎項目搭建
1) 7步構建maven框架
2)5步引入springboot依賴,選擇Building a RESTful Web Service
3)9步完成Mybatis接入SpringBoot項目。(其中創建數據庫一定要注意除了自增的id以外,其他欄若設置了非空,就一定要寫默認值!!!)
步數總結看該博主:
https://blog.csdn.net/midnight_time/article/details/90717676
代碼復制看該博主:
https://blog.csdn.net/m0_37657841/article/details/90524410
第三章 用戶模塊開發
1)MVC分層架構,dao已經自動生成,要加上service和controller
2)通用返回類型
之前springMVC的controller里面的方法是String為返回類型執行后return一個jsp頁面,也有viod的了,這里viod也要改為返回通用類型,數據為null即可,也有返回ModelAndView,后來@responsebody以后也可以直接把返回值為User對象直接返回給前端...(springMVCday2的response課,各種返回類型以及異常處理都有學,但是學完就忘好難過)。
這里是統一返回數據和狀態,數據的正確信息返回viewobject或null或者像驗證碼這樣的自定義返回類型(博主用了我們沒用),異常信息(異常信息包含errorCode和errorMsg)或空。有了這樣結構化的返回值,再加上@ResponseBody注解,就會被序列化為前端容易理解的JSON字符串。(而且不需要jkson解析json和Javabean的jar包??
3)otp驗證碼生成
controller就能完成,不需要數據庫。使用httpServletRequest.getSession().setAttribute(telphone, otpCode);在session域里面綁定手機號與驗證碼。(企業應該用redis)
4)登錄、注冊功能
需要與數據庫交互了。
發送驗證碼之后跳轉到注冊,注冊成功后跳轉到登錄。那么一開始應該是注冊登錄界面,不選登錄點擊注冊應該要跳轉到發送驗證碼。。
關於密碼表中用戶id要對應用戶表中的id的操作:
1.要在useDOMapper.xml中給insertSelective方法設置自增(所以userDO就有了id可以給Model?)
2.在UserModel轉換為密碼實體類 userPasswordDO的時候要設置密碼表中的userId : userPasswordDO.setUserId(userModel.getId());
3.userModel中的id也不可能一開始就有,畢竟用戶不會傳id,所以需要在userDO執行insertSelective方法之后,通過UserDO,從數據庫中查詢到當前id,然后賦值給userModel。讓userModel帶着它去構建userPasswordDO。 : userModel.setId(userDO.getId());
5)項目中的校驗邏輯
首先自定義一個校驗結果類,要是出錯了可以存出錯信息給通用返回類型給前端返回信息,然后一個實現 InitializingBean接口的實現類,就可以搭配注解使用了