【斗医】【11】Web应用开发20天

简介:
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://qingkechina.blog.51cto.com/5552198/1386941

本文在上文的基础上完成用户登录验证功能。

四、获取数据请求业务处理封装

1、配置数据读取方式,它的作用是使用FrameDataGainer响应以.data结尾的请求,并把处理后的数据返回给客户端。打开D:\medical\war\WEB-INF\web.xml文件,填充如下内容:

1
2
3
4
5
6
7
8
9
< 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的作用说明:

当浏览器通过uri下发XXX.data请求时,web容器tomcat将该请求交由FrameDataGainer处理,它通过getDataName()方法获取请求名XXX后,然后再从全局缓存FrameCache中根据XXX获取定义业务类名称,接着根据定义业务类名称把其反射出FrameDefaultAction对象,最后调用FrameDefaultAction对象的execute()进行具体的业务处理,并返回给客户端。这个过程与请求跳转封装类似,接下来跟着思维一步步实现这个过程:


3、定义FrameDataGainer类,让其继承HttpServlet对象,同时在init()方法中初始化配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public  class  FrameDataGainer  extends  HttpServlet
{
     /**
      * 定义日志对象
      */
     private  static  final  Logger logger = LoggerFactory.getLogger(FrameDataGainer. class );
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
     @Override
     public  void  init()
         throws  ServletException
     {
         ServletContext context = getServletContext();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
         // 加载业务配置文件
         try
         {
             FrameConfigUtil.loadDataBusiness(context);
         }
         catch  (FrameException e)
         {
             throw  new  ServletException( "[FrameDataGainer] init error." , e);
         }
     }
}


4、在FrameConfigUtil中定义loadDataBusiness()方法,通过该方法把xxx-data.xml等文件加载进入内存

1
2
3
4
5
6
7
8
/**
  * 加载D:\medical\war\WEB-INF\config下的所有数据业务配置文件
  */
  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中的配置加载到内存中。这里不再列出,感兴趣的话可以附件中查看源码。


5、FrameDataGainer.getDataName()方法用于把请求的动作名称从HTTP中解析出来:

1
2
3
4
5
6
7
8
9
public  class  FrameDataGainer  extends  HttpServlet
{
     @Override
     public  void  doGet(HttpServletRequest request, HttpServletResponse response)  throws  ServletException
     {
         String dataName = getDataName(request);
         // 省略
     }
}


6、当dataName为空时通过FrameDataGainer.getErrorResult()返回异常JSON对象,所以这里引入gson.jar

(1)进入https://code.google.com/p/google-gson/downloads/detail?name=google-gson-2.2.4-release.zip下载

(2)解压后把gson-2.2.4.jar复制到应用运行环境下的WEB-INF\lib下。我的环境位置为D:\medical\war\WEB-INF\lib

(3)在Eclipse工程中引入gson-2.2.4.jar

(4)定义com.medical.frame.bean.FrameResultBean.java对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public  class  FrameResultBean
{
     /**
      * 错误码
      */
     private  int  errorCode =  0 ;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
     /**
      * 错误描述
      */
     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()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
  * 返回请求异常Json对象
  */
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)请求数据名称没有配置返回异常JSON对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public  class  FrameDataGainer  extends  HttpServlet
{
     @Override
     public  void  doGet(HttpServletRequest request, HttpServletResponse response)  throws  ServletException
     {
         String dataName = getDataName(request);
         if  (dataName ==  null )
         {
             response.getWriter().write(getErrorResult());
             return ;
         }
     }
}


7、当请求数据名称存在时,根据数据名称从缓存中读取业务对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  FrameDataGainer  extends  HttpServlet
{
     @Override
     public  void  doGet(HttpServletRequest request, HttpServletResponse response)  throws  ServletException
     {
         // 省略
         FrameDataBusiness business = FrameCache.getInstance().getDataBusinessMap(dataName);
         if  (business ==  null )
         {
             response.getWriter().write(getErrorResult());
             return ;
         }
     }
}


8、从业务配置中反射出数据请求动作处理类对象,并把相应的处理结果写入Response对象中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  class  FrameDataGainer  extends  HttpServlet
{
     @Override
     public  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);
     }
}


由于描述可能不清楚,可以参见流程,其处理过程与页面跳转的封装类似:

wKioL1MxlGahQYJcAABBxZLgu3Q152.png


五、开发注册和登录功能

1、为让用户输入的密码不以明文显示,把D:\medical\war\module\login\login.html中的密码输入组件更改为password,如下:

1
< inputtype = "password" class = "login_user_input" placeholder = "请输入密码" id = "login_dynamic_user_pass" />


2、修改login.html文件中注册、登录部分代码如下:

1
2
3
4
< div  class = "login_button_wrapper" >
     < a  class = "login_confirm_button"  href = "javascript:systemUserLogin()" >登录</ a >
     < a  class = "login_regist_button"   href = "javascript:systemUserRegist()" >注册</ a >
</ div >


3、在D:\medical\war\js\login\login.js文件中完成按钮响应动作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
( 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. 用户名或密码长度不能超过20
         if (userName.length > 20 || userAuth.length > 20)
         {
             return ;
         }
         //3. 下发请求
         var  data = { "isRegist" : isRegist,  "userName" : userName,  "userAuth" : userAuth};
         asyncRequest( "login.data" , data,  function (result)
         {
             // TODO
             alert(1);
         });
     }
     /**
      * 对外公开方法
      */
     window.systemUserLogin = systemUserLogin;
     window.systemUserRegist = systemUserRegist;
})( window );


4、由于在第3步中调用了common.js中封装的asyncRequest()方法,这里把代码粘贴如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
  * 下发AJAX异步请求
  */
function  asyncRequest(action, param, callback)
{
     $.ajax(
     {
         type:  "GET" ,
         dataType:  "JSON" ,
         url: action,
         data: param,
         success:  function (result)
         {
             callback(result);
         }
     });
}


5、定义数据业务处理配置文件login-data.xml,并把其放置到D:\medical\war\WEB-INF\config\login下

1
2
3
4
<? xml  version = "1.0"  encoding = "UTF-8" ?>
< business-config >
     < business  name = "login"  business-class = "com.medical.server.data.UserLoginDataAction"  />
</ business-config >


6、通过第四步的封装和login-data.xml的定义,当在客户端输入用户名和密码后,点击登录或注册按钮,就可以调用到UserLoginDataAction业务处理类,其定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
  * 斗医系统用户登录逻辑处理类
  *
  * @author qingkechina
  */
public  class  UserLoginDataAction  extends  FrameDefaultAction
{
     /**
      * 全局Gson对象
      */
     private  final  static  Gson gson =  new  Gson();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
     /**
      * 缺省响应动作
      */
     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.用户名和密码的长度不能超过数据库字段的长度
         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.用户名和密码一般都是汉字、字母、数字和特殊字符
         // 这里留一个雷区,后面再讲解SQL注入时再进行完善
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
         // 4.判断是注册还是登录
         String isRegistUser = getParameter( "isRegist" );
         if  ( "TRUE" .equalsIgnoreCase(isRegistUser))
         {
             return  doRegistAction(userName, userAuth);
         }
         else
         {
             return  doLoginAction(userName, userAuth);
         }
     }
     // 略去doRegistAction()和doLoginAction()方法
}

【备注】:这里暂时不考虑用户信息安全,自棱镜门之后网络安全在国内得到越来越多的重视。用户注册登录的用户信息在客户端输入、传输、服务端验证、加解密等角度都需要考虑,这里暂时留一个彩蛋,以备后续安全专题时使用。


7、用户注册的处理逻辑:

先拿输入的用户名去数据库查找

(1)若查到说明存在重名的用户,请注册用户再选一个名称

(2)若查不到则把用户数据插入数据库,并返回注册成功信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
  * 用户注册处理方法
  */
private  String doRegistAction(String userName, String userAuth)
{
     // 1. 判断数据库中是否已存在该用户名
     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. 把用户入库
     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)、在《【斗医】【9】Web应用开发50天》文中,UserDao对象的数据映射文件主键定义了如下的样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<? 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 >

请留意<generator class="uuid.hex" />,它表明在向数据库插入记录时,Hibernate会调用uuid.hex的方式生成一个32位的主键;另一方面在创建数据表usertable时,指定userId字段的长度为20,所以入库时会抛字错内容这长的异常提示,这里把映射文件中的<generator class="uuid.hex" />删除掉,具体见附件。

(2)、再说一下Hibernate的保存问题,我在UserUtil.java中定义了保存UserDAO的一个方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
  * 把用户入库
  */
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();
}

网上有网友讨论保存不成功,因为他的写法如下:

Session session = FrameDBUtil.openSession();

session.save(userDao);

FrameDBUtil.closeSession();

因为这样的做法只是把userDao对象放入Hibernate缓存,而没有真正的入库。


8、用户登录的处理逻辑。先拿输入的用户名和密码去数据库查找,若查找到说明用户合法,否则用户非法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
  * 用户登录处理方法
  */
private  String doLoginAction(String userName, String userAuth)
{
     // 1. 判断数据库中是否已存在该用户名
     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对象
     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的相关方法,没有在文中专门指出,这里列出相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
  * 通过用户名称查找用户DAO对象
  */
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;
}
/**
  * 通过用户名和密码验证用户是否合法
  */
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收到服务端返回的结果后进行相应的逻辑处理

(1)若登录或注册失败,用户一定要看到失败信息,所以需要在war\etc\local\zh\resource.properties中定义相关的错误资源

1
2
3
500=用户名或密码错误
505=系统中已有同名用户
515=系统中不存在该用户


(2)完善war\js\login\login.js的异步请求,把从服务端收到的结果进行处理。当用户登录或注册成功后进入系统首页,否则弹框提示用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
  * 用户登录方法
  */
function  systemUserLogin(isRegist)
{
     var  userName = $( "#login_dynamic_user_name" ).val();
     var  userAuth = $( "#login_dynamic_user_pass" ).val();
     //1. 用户名或密码为空
     if (!userName || !userAuth)
     {
         return ;
     }
     //2. 用户名或密码长度不能超过20
     if (userName.length > 20 || userAuth.length > 20)
     {
         return ;
     }
     //3. 下发请求
     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 ;
         }
         // 跳转到相应页面
         top.location = resultJson.forwardPath;
     });
}

到此为至,用户的注册和登录已处理完毕,在Eclipse中运行Tomcat,把http://localhost:8080/medical敲入浏览器中后,点击登录出现如下界面:

wKioL1M3zojwMGIBAAATTMfID7U038.png

在该输入框中注册用户后页面可以正确地跳转到系统的首页,但细心的用户会发现系统首页的用户名那儿还是显示“登录”,而不是真正的用户名,由于51cto的这个编辑组件写的内容过长时,系统运行非常迟顿,所以在下一文处理这个问题。


【备注】:

(1)用户若在使用Hibernate过程中出现如下错误,说明是缺少antlr-3.1.2-1.jar.zip

java.lang.ClassNotFoundException: antlr.SemanticException

at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1462)

(2)截止目前系统所使用的jar包有如下,由于附件大小有限制,所以没有把jar悉数上传,若有需要的用户可以单独找我联系。

wKioL1M31APTDXYoAAAmy6XO13U498.png


本文出自 “青客” 博客,请务必保留此出处http://qingkechina.blog.51cto.com/5552198/1386941

目录
相关文章
|
9天前
|
数据库 开发者 Python
web应用开发
【9月更文挑战第1天】web应用开发
24 1
|
6天前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践
|
7天前
|
前端开发 JavaScript 持续交付
Web应用开发的方法
Web应用开发的方法
10 1
|
7天前
|
前端开发 JavaScript 持续交付
web应用开发
web应用开发
17 1
|
9天前
|
Web App开发 数据采集 iOS开发
「Python+Dash快速web应用开发」
这是「Python+Dash快速web应用开发」系列教程的第十六期,本期将介绍如何在Dash应用中实现多页面路由功能。通过使用`Location()`组件监听URL变化,并结合回调函数动态渲染不同页面内容,使应用更加模块化和易用。教程包括基础用法、页面重定向及无缝跳转等技巧,并通过实例演示如何构建个人博客网站。
20 2
WK
|
6天前
|
数据采集 API 开发者
很少有人用python开发web???
Python 是一种流行且广泛使用的编程语言,尤其在 Web 开发领域。它凭借简洁的语法、丰富的框架(如 Django 和 Flask)、强大的库支持及活跃的社区,成为许多开发者和企业的首选。Python 的易学性、高效性及广泛的应用场景(包括 Web API、微服务和数据分析等)使其在 Web 开发中占据重要地位,并将持续发挥更大作用。
WK
24 0
|
10天前
|
数据库 开发者 Java
数据战争:Hibernate的乐观与悲观锁之争,谁将主宰并发控制的王座?
【8月更文挑战第31天】在软件开发中,数据一致性至关重要,尤其是在多用户并发访问环境下。Hibernate 作为 Java 社区常用的 ORM 框架,提供了乐观锁和悲观锁机制来处理并发问题。乐观锁假设数据不易冲突,通过版本号字段 (`@Version`) 实现;悲观锁则假定数据易冲突,在读取时即加锁。选择哪种锁取决于具体场景:乐观锁适合读多写少的情况,减少锁开销;悲观锁适合写操作频繁的场景,避免数据冲突。正确应用这些机制可提升应用程序的健壮性和效率。
20 0
|
10天前
|
Java 测试技术 容器
从零到英雄:Struts 2 最佳实践——你的Web应用开发超级变身指南!
【8月更文挑战第31天】《Struts 2 最佳实践:从设计到部署的全流程指南》深入介绍如何利用 Struts 2 框架从项目设计到部署的全流程。从初始化配置到采用 MVC 设计模式,再到性能优化与测试,本书详细讲解了如何构建高效、稳定的 Web 应用。通过最佳实践和代码示例,帮助读者掌握 Struts 2 的核心功能,并确保应用的安全性和可维护性。无论是在项目初期还是后期运维,本书都是不可或缺的参考指南。
19 0
|
10天前
|
前端开发 开发者 安全
JSF面向对象设计,让你轻松应对复杂业务需求,Web应用开发不再难!
【8月更文挑战第31天】在现代Web应用开发中,JSF(JavaServer Faces)框架凭借其强大的面向对象编程能力广泛应用于数据绑定和事件处理。数据绑定机制使前端与后端模型解耦,提高代码维护性和类型安全性;事件处理机制则增强了应用灵活性并进一步降低耦合度。本文通过示例代码展示了JSF的这些特性及其优势,帮助开发者更好地利用JSF构建高效、灵活的Web应用。然而,JSF也存在组件库较小和学习成本较高的局限,需根据具体需求权衡使用。
21 0
|
10天前
|
开发者 iOS开发 C#
Uno Platform 入门超详细指南:从零开始教你打造兼容 Web、Windows、iOS 和 Android 的跨平台应用,轻松掌握 XAML 与 C# 开发技巧,快速上手示例代码助你迈出第一步
【8月更文挑战第31天】Uno Platform 是一个基于 Microsoft .NET 的开源框架,支持使用 C# 和 XAML 构建跨平台应用,适用于 Web(WebAssembly)、Windows、Linux、macOS、iOS 和 Android。它允许开发者共享几乎全部的业务逻辑和 UI 代码,同时保持原生性能。选择 Uno Platform 可以统一开发体验,减少代码重复,降低开发成本。安装时需先配置好 Visual Studio 或 Visual Studio for Mac,并通过 NuGet 或官网下载工具包。
18 0
下一篇
DDNS