【斗医】【11】Web应用开发20天
本文在上文的基礎(chǔ)上完成用戶登錄驗(yàn)證功能。
四、獲取數(shù)據(jù)請(qǐng)求業(yè)務(wù)處理封裝
1、配置數(shù)據(jù)讀取方式,它的作用是使用FrameDataGainer響應(yīng)以.data結(jié)尾的請(qǐng)求,并把處理后的數(shù)據(jù)返回給客戶端。打開(kāi)D:\medical\war\WEB-INF\web.xml文件,填充如下內(nèi)容:
<servlet><servlet-name>data</servlet-name><servlet-class>com.medical.frame.FrameDataGainer</servlet-class><load-on-startup>2</load-on-startup> </servlet> <servlet-mapping><servlet-name>data</servlet-name><url-pattern>*.data</url-pattern> </servlet-mapping>
2、FrameDataGainer的作用說(shuō)明:
當(dāng)瀏覽器通過(guò)uri下發(fā)XXX.data請(qǐng)求時(shí),web容器tomcat將該請(qǐng)求交由FrameDataGainer處理,它通過(guò)getDataName()方法獲取請(qǐng)求名XXX后,然后再?gòu)娜志彺鍲rameCache中根據(jù)XXX獲取定義業(yè)務(wù)類名稱,接著根據(jù)定義業(yè)務(wù)類名稱把其反射出FrameDefaultAction對(duì)象,最后調(diào)用FrameDefaultAction對(duì)象的execute()進(jìn)行具體的業(yè)務(wù)處理,并返回給客戶端。這個(gè)過(guò)程與請(qǐng)求跳轉(zhuǎn)封裝類似,接下來(lái)跟著思維一步步實(shí)現(xiàn)這個(gè)過(guò)程:
3、定義FrameDataGainer類,讓其繼承HttpServlet對(duì)象,同時(shí)在init()方法中初始化配置類
public?class?FrameDataGainer?extends?HttpServlet {/***?定義日志對(duì)象*/private?static?final?Logger?logger?=?LoggerFactory.getLogger(FrameDataGainer.class);@Overridepublic?void?init()throws?ServletException{ServletContext?context?=?getServletContext();//?加載業(yè)務(wù)配置文件try{FrameConfigUtil.loadDataBusiness(context);}catch?(FrameException?e){throw?new?ServletException("[FrameDataGainer]?init?error.",?e);}} }
4、在FrameConfigUtil中定義loadDataBusiness()方法,通過(guò)該方法把xxx-data.xml等文件加載進(jìn)入內(nèi)存
/***?加載D:\medical\war\WEB-INF\config下的所有數(shù)據(jù)業(yè)務(wù)配置文件*/public?static?void?loadDataBusiness(ServletContext?context)?throws?FrameException{findDataFile(context,?"/WEB-INF/config");parseDataBusiness(context);}
其中findDataFile()用于查找D:\medical\war\WEB-INF\config下所有的xxx-data.xml文件;parseDataBusiness()用于把xxx-data.xml中的配置加載到內(nèi)存中。這里不再列出,感興趣的話可以附件中查看源碼。
5、FrameDataGainer.getDataName()方法用于把請(qǐng)求的動(dòng)作名稱從HTTP中解析出來(lái):
public?class?FrameDataGainer?extends?HttpServlet {@Overridepublic?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException{String?dataName?=?getDataName(request);//?省略} }
6、當(dāng)dataName為空時(shí)通過(guò)FrameDataGainer.getErrorResult()返回異常JSON對(duì)象,所以這里引入gson.jar
(1)進(jìn)入https://code.google.com/p/google-gson/downloads/detail?name=google-gson-2.2.4-release.zip下載
(2)解壓后把gson-2.2.4.jar復(fù)制到應(yīng)用運(yùn)行環(huán)境下的WEB-INF\lib下。我的環(huán)境位置為D:\medical\war\WEB-INF\lib
(3)在Eclipse工程中引入gson-2.2.4.jar
(4)定義com.medical.frame.bean.FrameResultBean.java對(duì)象
public?class?FrameResultBean {/***?錯(cuò)誤碼*/private?int?errorCode?=?0;/***?錯(cuò)誤描述*/private?String?errorDesc?=?null;public?int?getErrorCode(){return?errorCode;}public?void?setErrorCode(int?errorCode){this.errorCode?=?errorCode;}public?String?getErrorDesc(){return?errorDesc;}public?void?setErrorDesc(String?errorDesc){this.errorDesc?=?errorDesc;} }
(5)定義FrameDataGainer.getErrorResult()方法
/***?返回請(qǐng)求異常Json對(duì)象*/ private?String?getErrorResult() {FrameResultBean?resultBean?=?new?FrameResultBean();resultBean.setErrorCode(FrameErrorCode.REQUEST_BUS_NOT_EXIST);String?errorCode?=?String.valueOf(FrameErrorCode.REQUEST_BUS_NOT_EXIST);String?errorDesc?=?FrameCache.getInstance().getResourceValue(errorCode);resultBean.setErrorDesc(errorDesc);return?gson.toJson(resultBean); }
(6)請(qǐng)求數(shù)據(jù)名稱沒(méi)有配置返回異常JSON對(duì)象
public?class?FrameDataGainer?extends?HttpServlet {@Overridepublic?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException{String?dataName?=?getDataName(request);if?(dataName?==?null){response.getWriter().write(getErrorResult());return;}} }
7、當(dāng)請(qǐng)求數(shù)據(jù)名稱存在時(shí),根據(jù)數(shù)據(jù)名稱從緩存中讀取業(yè)務(wù)對(duì)象
public?class?FrameDataGainer?extends?HttpServlet {@Overridepublic?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException{//?省略FrameDataBusiness?business?=?FrameCache.getInstance().getDataBusinessMap(dataName);if?(business?==?null){response.getWriter().write(getErrorResult());return;}} }
8、從業(yè)務(wù)配置中反射出數(shù)據(jù)請(qǐng)求動(dòng)作處理類對(duì)象,并把相應(yīng)的處理結(jié)果寫(xiě)入Response對(duì)象中
public?class?FrameDataGainer?extends?HttpServlet {@Overridepublic?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException{//?省略FrameDefaultAction?action?=?FrameActionFactory.getInstance().implement(business.getBusinessClass());action.setRequest(request);action.setResponse(response);action.setSession(request.getSession());String?resultData?=?action.execute();response.getWriter().write(resultData?==?null???getErrorResult()?:?resultData);} }
由于描述可能不清楚,可以參見(jiàn)流程,其處理過(guò)程與頁(yè)面跳轉(zhuǎn)的封裝類似:
五、開(kāi)發(fā)注冊(cè)和登錄功能
1、為讓用戶輸入的密碼不以明文顯示,把D:\medical\war\module\login\login.html中的密碼輸入組件更改為password,如下:
<inputtype="password"class="login_user_input"placeholder="請(qǐng)輸入密碼"id="login_dynamic_user_pass"/>2、修改login.html文件中注冊(cè)、登錄部分代碼如下:
<div?class="login_button_wrapper"><a?class="login_confirm_button"?href="javascript:systemUserLogin()">登錄</a><a?class="login_regist_button"??href="javascript:systemUserRegist()">注冊(cè)</a> </div>3、在D:\medical\war\js\login\login.js文件中完成按鈕響應(yīng)動(dòng)作:
(function(?window){/***?用戶登錄方法*/function?systemUserLogin(isRegist){var?userName?=?$("#login_dynamic_user_name").val();var?userAuth?=?$("#login_dynamic_user_pass").val();//1.?用戶名或密碼為空if(!userName?||?!userAuth){return;}//2.?用戶名或密碼長(zhǎng)度不能超過(guò)20if(userName.length?>?20?||?userAuth.length?>?20){return;}//3.?下發(fā)請(qǐng)求var?data?=?{"isRegist":?isRegist,?"userName":?userName,?"userAuth":?userAuth};asyncRequest("login.data",?data,?function(result){//?TODOalert(1);});}/***?對(duì)外公開(kāi)方法*/window.systemUserLogin?=?systemUserLogin;window.systemUserRegist?=?systemUserRegist; })(?window?);
4、由于在第3步中調(diào)用了common.js中封裝的asyncRequest()方法,這里把代碼粘貼如下:
/***?下發(fā)AJAX異步請(qǐng)求*/ function?asyncRequest(action,?param,?callback) {$.ajax({type:?"GET",dataType:?"JSON",url:?action,data:?param,success:?function(result){callback(result);}}); }5、定義數(shù)據(jù)業(yè)務(wù)處理配置文件login-data.xml,并把其放置到D:\medical\war\WEB-INF\config\login下
<?xml?version="1.0"?encoding="UTF-8"?> <business-config><business?name="login"?business-class="com.medical.server.data.UserLoginDataAction"?/> </business-config>
6、通過(guò)第四步的封裝和login-data.xml的定義,當(dāng)在客戶端輸入用戶名和密碼后,點(diǎn)擊登錄或注冊(cè)按鈕,就可以調(diào)用到UserLoginDataAction業(yè)務(wù)處理類,其定義如下:
/***?斗醫(yī)系統(tǒng)用戶登錄邏輯處理類**?@author?qingkechina*/ public?class?UserLoginDataAction?extends?FrameDefaultAction {/***?全局Gson對(duì)象*/private?final?static?Gson?gson?=?new?Gson();/***?缺省響應(yīng)動(dòng)作*/public?String?execute()throws?FrameException{//?1.從客戶端獲取用戶名和密碼String?userName?=?getParameter("userName");String?userAuth?=?getParameter("userAuth");if?(FrameUtil.isEmpty(userName)?||?FrameUtil.isEmpty(userAuth)){UserLoginBean?loginBean?=?new?UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return?gson.toJson(loginBean);}//?2.用戶名和密碼的長(zhǎng)度不能超過(guò)數(shù)據(jù)庫(kù)字段的長(zhǎng)度if?(userName.length()?>?20?||?userAuth.length()?>?64){UserLoginBean?loginBean?=?new?UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return?gson.toJson(loginBean);}//?3.用戶名和密碼一般都是漢字、字母、數(shù)字和特殊字符//?這里留一個(gè)雷區(qū),后面再講解SQL注入時(shí)再進(jìn)行完善//?4.判斷是注冊(cè)還是登錄String?isRegistUser?=?getParameter("isRegist");if?("TRUE".equalsIgnoreCase(isRegistUser)){return?doRegistAction(userName,?userAuth);}else{return?doLoginAction(userName,?userAuth);}}//?略去doRegistAction()和doLoginAction()方法 }
【備注】:這里暫時(shí)不考慮用戶信息安全,自棱鏡門(mén)之后網(wǎng)絡(luò)安全在國(guó)內(nèi)得到越來(lái)越多的重視。用戶注冊(cè)登錄的用戶信息在客戶端輸入、傳輸、服務(wù)端驗(yàn)證、加解密等角度都需要考慮,這里暫時(shí)留一個(gè)彩蛋,以備后續(xù)安全專題時(shí)使用。
7、用戶注冊(cè)的處理邏輯:
先拿輸入的用戶名去數(shù)據(jù)庫(kù)查找
(1)若查到說(shuō)明存在重名的用戶,請(qǐng)注冊(cè)用戶再選一個(gè)名稱
(2)若查不到則把用戶數(shù)據(jù)插入數(shù)據(jù)庫(kù),并返回注冊(cè)成功信息
/***?用戶注冊(cè)處理方法*/ private?String?doRegistAction(String?userName,?String?userAuth) {//?1.?判斷數(shù)據(jù)庫(kù)中是否已存在該用戶名UserDAO?user?=?UserUtil.getUserByName(userName);if(user?!=?null){UserLoginBean?loginBean?=?new?UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_SAME_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return?gson.toJson(loginBean);}//?2.?把用戶入庫(kù)UserUtil.insertUser(userName,?userAuth);UserLoginBean?loginBean?=?new?UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_REGIST_SUCCESS);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));loginBean.setForwardPath("main.act");return?gson.toJson(loginBean); }
【備注】:
(1)、在《【斗醫(yī)】【9】Web應(yīng)用開(kāi)發(fā)50天》文中,UserDao對(duì)象的數(shù)據(jù)映射文件主鍵定義了如下的樣式:
<?xml?version="1.0"?encoding="utf-8"?> <!DOCTYPE?hibernate-mapping?PUBLIC"-//Hibernate/Hibernate?Mapping?DTD?3.0//EN""http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping?package="com.medical.server.dao"><class?name="UserDAO"?table="USERTABLE"><id?name="userId"?column="userId"?type="string"><generator?class="uuid.hex"?/></id><property?name="userAuth"?column="userAuth"?/><property?name="userSign"?column="userSign"?/><property?name="attention"?column="attention"?/></class> </hibernate-mapping>
請(qǐng)留意<generator class="uuid.hex" />,它表明在向數(shù)據(jù)庫(kù)插入記錄時(shí),Hibernate會(huì)調(diào)用uuid.hex的方式生成一個(gè)32位的主鍵;另一方面在創(chuàng)建數(shù)據(jù)表usertable時(shí),指定userId字段的長(zhǎng)度為20,所以入庫(kù)時(shí)會(huì)拋?zhàn)皱e(cuò)內(nèi)容這長(zhǎng)的異常提示,這里把映射文件中的<generator class="uuid.hex" />刪除掉,具體見(jiàn)附件。
(2)、再說(shuō)一下Hibernate的保存問(wèn)題,我在UserUtil.java中定義了保存UserDAO的一個(gè)方法,如下:
/***?把用戶入庫(kù)*/ public?static?void?insertUser(String?userName,?String?userAuth) {UserDAO?userDao?=?new?UserDAO();userDao.setUserId(userName);userDao.setUserAuth(userAuth);Session?session?=?FrameDBUtil.openSession();Transaction?transaction?=?session.beginTransaction();session.save(userDao);transaction.commit();FrameDBUtil.closeSession(); }
網(wǎng)上有網(wǎng)友討論保存不成功,因?yàn)樗膶?xiě)法如下:
Session session = FrameDBUtil.openSession();
session.save(userDao);
FrameDBUtil.closeSession();
因?yàn)檫@樣的做法只是把userDao對(duì)象放入Hibernate緩存,而沒(méi)有真正的入庫(kù)。
8、用戶登錄的處理邏輯。先拿輸入的用戶名和密碼去數(shù)據(jù)庫(kù)查找,若查找到說(shuō)明用戶合法,否則用戶非法
/***?用戶登錄處理方法*/ private?String?doLoginAction(String?userName,?String?userAuth) {//?1.?判斷數(shù)據(jù)庫(kù)中是否已存在該用戶名boolean?isValideUser?=?UserUtil.isValideUser(userName,?userAuth);if?(isValideUser?==?false){UserLoginBean?loginBean?=?new?UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_NOT_EXIST_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return?gson.toJson(loginBean);}//?2.返回用戶登錄成功JSON對(duì)象UserLoginBean?loginBean?=?new?UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_SUCCESS);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));loginBean.setForwardPath("main.act");return?gson.toJson(loginBean); }
【備注】:
由于涉及到UserUtil.java的相關(guān)方法,沒(méi)有在文中專門(mén)指出,這里列出相關(guān)代碼
/***?通過(guò)用戶名稱查找用戶DAO對(duì)象*/ public?static?UserDAO?getUserByName(String?userName) {Session?session?=?FrameDBUtil.openSession();Criteria?criteria?=?session.createCriteria(UserDAO.class);criteria.add(Restrictions.eq("userId",?userName));List<?>?userList?=?criteria.list();FrameDBUtil.closeSession();if?(FrameUtil.isEmpty(userList)){return?null;}UserDAO?userDao?=?(UserDAO)userList.get(0);return?userDao; } /***?通過(guò)用戶名和密碼驗(yàn)證用戶是否合法*/ public?static?boolean?isValideUser(String?userName,?String?userAuth) {Session?session?=?FrameDBUtil.openSession();Criteria?criteria?=?session.createCriteria(UserDAO.class);criteria.add(Restrictions.eq("userId",?userName)).add(Restrictions.eq("userAuth",?userAuth));List<?>?userList?=?criteria.list();FrameDBUtil.closeSession();if?(FrameUtil.isEmpty(userList)){return?false;}return?true; }8、客戶端JS收到服務(wù)端返回的結(jié)果后進(jìn)行相應(yīng)的邏輯處理
(1)若登錄或注冊(cè)失敗,用戶一定要看到失敗信息,所以需要在war\etc\local\zh\resource.properties中定義相關(guān)的錯(cuò)誤資源
500=用戶名或密碼錯(cuò)誤 505=系統(tǒng)中已有同名用戶 515=系統(tǒng)中不存在該用戶
(2)完善war\js\login\login.js的異步請(qǐng)求,把從服務(wù)端收到的結(jié)果進(jìn)行處理。當(dāng)用戶登錄或注冊(cè)成功后進(jìn)入系統(tǒng)首頁(yè),否則彈框提示用戶
/***?用戶登錄方法*/ function?systemUserLogin(isRegist) {var?userName?=?$("#login_dynamic_user_name").val();var?userAuth?=?$("#login_dynamic_user_pass").val();//1.?用戶名或密碼為空if(!userName?||?!userAuth){return;}//2.?用戶名或密碼長(zhǎng)度不能超過(guò)20if(userName.length?>?20?||?userAuth.length?>?20){return;}//3.?下發(fā)請(qǐng)求var?data?=?{"isRegist":?isRegist,?"userName":?userName,?"userAuth":?userAuth};asyncRequest("login.data",?data,?function(result){var?resultJson?=?eval(result);if(resultJson.errorCode?!=?510){alert(resultJson.errorDesc);return;}//?跳轉(zhuǎn)到相應(yīng)頁(yè)面top.location?=?resultJson.forwardPath;}); }
到此為至,用戶的注冊(cè)和登錄已處理完畢,在Eclipse中運(yùn)行Tomcat,把http://localhost:8080/medical敲入瀏覽器中后,點(diǎn)擊登錄出現(xiàn)如下界面:
在該輸入框中注冊(cè)用戶后頁(yè)面可以正確地跳轉(zhuǎn)到系統(tǒng)的首頁(yè),但細(xì)心的用戶會(huì)發(fā)現(xiàn)系統(tǒng)首頁(yè)的用戶名那兒還是顯示“登錄”,而不是真正的用戶名,由于51cto的這個(gè)編輯組件寫(xiě)的內(nèi)容過(guò)長(zhǎng)時(shí),系統(tǒng)運(yùn)行非常遲頓,所以在下一文處理這個(gè)問(wèn)題。
【備注】:
(1)用戶若在使用Hibernate過(guò)程中出現(xiàn)如下錯(cuò)誤,說(shuō)明是缺少antlr-3.1.2-1.jar.zip包
java.lang.ClassNotFoundException: antlr.SemanticException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1462)
(2)截止目前系統(tǒng)所使用的jar包有如下,由于附件大小有限制,所以沒(méi)有把jar悉數(shù)上傳,若有需要的用戶可以單獨(dú)找我聯(lián)系。
轉(zhuǎn)載于:https://blog.51cto.com/qingkechina/1386941
總結(jié)
以上是生活随笔為你收集整理的【斗医】【11】Web应用开发20天的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: LVS+Keepalived实现高可用集
- 下一篇: Core Services层