浅谈Web应用开发

随着浏览器能力的增强,绝大部分工作任务都是基于浏览器的应用开发。从2008年底,我第一次接触Web应用开发至今,已接触过Web Form、MVC、前后端分离等不同的开发模式,近期也在从事基于vue.js+Spring Cloud的Web应用开发。在Web应用开发方面积累了一些经验,在此与大家分享。由于一直都是“做中学”,以下描述可能不够专业、不够系统,所述如有不妥之处,欢迎拍砖指正。

 什么是Web应用 

“Web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件。(引自百度词条)更通俗地说,就是我们日常所访问的网站。

传统上,Web应用将静态资源和动态数据请求打在一个包中,如以jsp开发的Web应用中,jsp页面和后台数据处理逻辑通常打在一个war包中。当前,Web应用的开发通常采用前后端分离的开发模式,即前端开发人员开发html/css/js等静态资源,后端开发人员提供数据服务接口(如rest api)。在这种模式下,浏览器加载静态资源后,通过发起数据请求访问服务端提供的数据服务,其好处在于,让开发人员专注于自己擅长的工作。

作为Web应用开发人员应该对图中所示非常熟悉。接下来,根据与日常讨论中,部分同事遇到的问题做一些简单总结。

浏览器是如何工作的

从用户输入域名到看到页面,浏览器主要做了以下几件事:

1.DNS解析

用户输入URL后,浏览器“询问”DNS服务器此域名对应的IP地址,这里不展开讨论DNS服务器如何将域名转换为IP地址。

2.发起HTTP请求

确定此域名对应的IP后,浏览器根据IP地址和端口与目标服务器进行建立TCP连接。根据URL中的协议、Path等,通过已建立的TCP连接发起HTTP请求,并等待服务端的响应。

3.渲染

浏览器将服务端响应解析成用户能够理解的页面:

浏览器可以解析html,加载标记中包含的额外资源(如js文件或css文件等),并将它们呈现给用户;

浏览器加载的js也可以发起ajax请求,js根据响应的数据动态修改html的内容。

(更多关于浏览器的渲染原理的内容,参见《浏览器的工作原理:新式网络浏览器幕后揭秘》https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/

 关于浏览器的其他事项 

1.HTTP协议

浏览器通常以HTTP协议与服务端进行数据交互。HTTP协议是单向的请求-响应协议,浏览器封装HTTP请求发送给服务器,服务器接收到请求后,将响应数据封装成HTTP响应返回给浏览器,若没有请求就没有响应。

同时,HTTP协议也是无状态的协议,即在协议层面,无法辨别两个请求的关联关系,如协议本身没有机制将登录请求后续的数据请求绑定。在实践中,通常有两个方式来保持这种“状态”:

(1)客户端方式

将状态信息存储于浏览器,服务端不存储状态信息,每次的HTTP请求都带上此状态信息,最常用的携带方式是置于cookie中,因为浏览器每次发起HTTP请求都会自动带上“可携带”的cookie。

例如,现在业界常用的JWT(JSON Web Token)方案,在验证用户的密码后,服务端将生成一个JSON对象并将其发送回用户,浏览器在发起后续的请求中发回JSON对象,服务端通过此JSON对象的内容将多个请求关联到同一个用户上。为了防止用户篡改数据,服务端将对JSON对象进行签名,甚至加密。

需要注意的是,由于apache等服务端对header的长度有限制,JWT对象的内容不宜过长,否则apache等服务端将响应状态码为400的报文。

(2)服务端方式

将状态信息存储于服务端session,只将sessionID返回给浏览器,每次的HTTP请求都带上此sessionID。例如在cookie中带上sessionID,服务端从cookie中取出sessionID并以此在存储中查找对应的状态信息。这种方式的问题在于,对于分布式架构,需要共享存储(如将状态信息存储于redis中)的支持。

2.Websocket

随着Web应用的功能日益丰富,只能先有请求才有响应的HTTP协议已无法满足用户需求,于是Websocket协议便应运而生。Websocket协议是双向的数据通信协议,支持服务端主动向浏览器推送消息,大部分现代浏览器都已支持Websocket协议。在需要消息推送的场景(如web聊天室),可以尝试Websocket协议的方式实现。

3.同源策略

所谓同源是指域名,协议,端口相同

同源策略是浏览器最核心最基本的安全机制,即在没有明确授权的情况下,限制非同源的document读取或设置当前document的某些属性。

例如,在没有明确授权的情况下

页面可以向任何服务端发起请求,但是收到服务端响应后,将检查响应是否与自己同源,如果收到非同源的数据将报错;

请求中只携带同源的cookie;

JS脚本只允许读取或设置同源的cookie、SessionStorage、LocalStorage和IndexDB等数据。

在实践中,有多种方式绕过同源策略的限制,但是,作为浏览器最基本的安全机制不建议在浏览器端轻易放开此限制。

前后端分离的服务端实现

自2008年底接触Web应用开发至今,我先后接触了WebForm、MVC、前后端分离等不同的开发模式。在最近的实践中,我们采用了前后端分离的开发模式,这也是当前最流行的开发模式,因此这里仅介绍前后端分离的服务端实现。

浅谈Web应用开发_第1张图片

如图所示,是我们通常采用的模块划分方案,包括反向代理、静态资源和Java后端服务。其中,反向代理用于解决浏览器同源策略的限制,在服务端对静态资源和数据服务请求进行分离,这样对于浏览器来说,静态资源和数据服务的请求就在同域下。

1.Web前端开发

目前,前端开发流行三大框架:VueJs、AngularJs和ReactJs。对比三个框架,VueJs上手简单、社区活跃、中文文档丰富,作为快餐主义者,自然而然地选择了VueJs。具体的vueJs开发学习内容,建议直接参考官方文档https://cn.vuejs.org/v2/guide/,这里不再赘述。前端开发完成的静态资源经打包压缩后,托管于Web服务器,通常的Web服务器有Apache、Nginx,前端页面通过事先定义的数据服务接口与后端服务交换数据。

2.Java后端开发

与前几年流行的SSH框架不同,当前Java后端开发中,基于Spring Cloud的应用开发占据了大半江山。Spring Cloud作为第二代微服务的代表性框架,是一个基于Spring Boot实现的、用于快速构建分布式架构的应用的工具集,其中涉及配置管理、服务发现、熔断器、路由转发等诸多工具。我的第一本Spring Boot入门书籍是《JavaEE开发的颠覆者: Spring Boot实战》,个人认为是一本不错的入门书籍。

在图中所示的架构中,各模块的功能概述如下:

认证与权限管理服务负责与个性化的认证系统(UASS)、权限管理系统(机构员工)对接,避免了各子服务重复对接的工作。

网关与认证、权限系统进行对接,负责访问控制、路由转发、负载均衡和流量控制,承担了传统应用的SessionFilter的作用。

业务应用收到网关转发的请求,即已通过了访问控制,只需基于网关提供的用户信息专注于业务逻辑的实现。

这种架构的优势在于,在产品输出过程中,面对不同的认证与权限管理体系,只需在认证与权限管理服务做相应适配,业务应用的实现逻辑可基本保持不变,大大提高了系统的适应性、可扩展性。当然,此架构中仍缺少配置中心、监控中心等服务模块,也缺少服务间安全调用方案(安全性仍依赖于网络隔离),对于这些模块,我们也还在积极探索中。

 Nginx的使用提示 

实践中,最常用的Web服务器有Apache和Nginx。Nginx以其轻量级、占用资源少、并发能力强、使用方便(配置简单、重启方便),既可以作为反向代理,也可以用作Web服务器,腾讯、淘宝、京东、百度、新浪、网易等大厂都在使用Nginx。在公有云系统的部署中,我们也大量使用Nginx(相比之下,Apache的配置实在太麻烦)。

各种资料中都有Nginx配置的详细介绍http://www.nginx.cn/doc/,这里介绍我们在使用Nginx过程中曾遇到的几个不容发现的问题。

1.Resolver

通常,内网和互联网是隔离的,当内网访问互联网服务时可在Nginx配置反向代理,如:

location/ccb {

proxy_passhttp://www.ccb.com/;

}

此时,内网仍然无法调用互联网服务,需要配置resolver,告诉Nginx应通过哪个DNS服务器解析www.ccb.com的域名。

2.$http_host

location/gateway {

proxy_passhttp://gateway;

proxy_set_header Host $host;

}

当后端gateway需要将用户重定向到同域下的其他页面时,通常重定向参数只设置路径而不提供IP和端口,如果采用上述配置,重定向后将丢失参数。如用户访问http://portal:8080/xxx,gateway收到请求后将用户重定向到/yyy,则用户将被重定向到http://portal/yyy,端口8080将丢失。此时只需要做如下修改即可。

location/gateway {

proxy_passhttp://gateway;

proxy_set_headerHost $http_host;

}

3.Proxy_pass

location/ccb {

proxy_passhttp://www.ccb.com/; #(1)

proxy_passhttp://www.ccb.com; #(2)

}

在配置中注意上述两种情况的差异,如果请求路径为/ccb/xxx,

对于(1),转发后的请求为http://www.ccb.com/xxx;

对于(2),转发后的请求为http://www.ccb.com/ccb/xxx;

你可能感兴趣的:(浅谈Web应用开发)