深入浅出Web开发

发布于 2021-09-04 17:44 ,所属分类:软件编程学习资料

Web开发

Web开发,通俗一点来说就是做网站。学术一点来说,是做B/S架构的软件,B指Browser浏览器,S指服务端Server。

浏览器(HTML、JavaScript) <—> HTTP协议 <—> 服务器(PHP、Python、Java...)

用户打开网址,服务器返回HTML,浏览器解析HTMl图形化展示给用户内容。用户提交表单,经过HTTP协议传输,服务收到来自用户的数据,数据交给应用程序处理,返回给用户结果。

移动互联网时代,这个架构也许是这样。

APP(ios,安卓) <—> HTTP协议 <—> 服务器(PHP、Python、Java...)

B(Browser)变成了C(Client),也叫C/S架构。

现在的Web开发,按照这个架构HTTP协议的两头分为前端、后端,本文我们侧重后端。

HTTP协议

我们先从架构中的网络部分开始,即HTTP协议rfc2616。

HTTP协议中定义了两个角色客户端和服务端:

  • 客户端,发起请求的实体
  • 服务端,响应请求的实体

在B/S架构中,客户端便是我们的浏览器,服务端是我们服务器软件。

常见的浏览器有:Chrome、Firefox、IE等。常见的服务器软件有:Apache2、Nginx等。

这些软件已经帮我们实现了,Web体系中面向网络的,网络编程部分工作,比如建立连接、解析HTTP报文、提取数据等。

应用网关协议

在Web后端中,服务器软件档在了前面,处理网络连接和数据接收解析。

我们开发业务系统,其实工作在这些Web服务器的后面,业务系统与Web服务器交互。

Browser <—HTTP协议—> Web Server <——> 业务系统(PHP,Python,Java...)

我们先看一个普通的程序

def get_user_info(user_id):
user = db.query("select * from user where id=?",user_id)
return {
"result": user
}

问:我们返回的结果如何交付给Web服务器?Web服务器如何执行这段Python代码?

答:应用网关协议!

Browser <—HTTP协议—> Web Server <—应用网关协议—> 业务系统(PHP,Python,Java...)

应用网关协议定义了业务系统如何与Web服务器交互,比如Web服务器如何将用户传来的参数传给业务系统,业务系统如何交付输出给Web服务器。

目前为止不同Web服务器,编程语言出现了很多应用网关协议,比如CGI、FastCGI、Python的WSGI,Java的Servlet等等。

我们以最古老和简单的CGI(Common Gateway Interface)为例rfc3875。

CGI的工作原理非常简单,业务系统是一个简单的可执行二进制或脚本文件,当请求到来,Web服务器fork子进程执行这个可执行程序,请求数据通过Stdin和环境变量传入,然后接受子进程执行结果的Stdout,最后将结果装入HTTP响应报文返回给客户端。

Browser <—HTTP协议—> Web Server <—CGI—> exec(get_user.py, env,stdin, stdout)

将我们上面的程序封装成CGI Web程序大概这样子。

def get_user_info(user_id):
user = db.query("select * from user where id=?",user_id)
return {
"result": user
}

# 从环境变量获取请求参数
query_string = get_env("QUERY_STRING")

uid = get_args_from_query(query_string)

# 执行业务逻辑查找用户
user = get_user_info(uid)

# 输出用户数据到stdout
import json
print ("Content-type:application/json")
print ()
print(json.dumps({
"result":user
}))

在Ubuntu下以Apache2和Python为例,做一个CGI的例子。

为什么不用Nginx?因为Nginx默认模块不支持CGI了,写本文时候也想用Nginx,发现怪麻烦的。

启用CGI模块

/etc/apache2/mods-enabled
ln -s ../mods-available/cgid.load .
ln -s ../mods-available/cgid.conf .

配置Vhost和CGI脚本路径

# 基于default修改
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /home/www/play

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

<IfModule mod_alias.c> # 添加cgi模块
<IfModule mod_cgi.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>

<IfModule mod_cgid.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>

<IfDefine ENABLE_USR_LIB_CGI_BIN>
ScriptAlias /cgi-bin/ /home/www/play/cgi-bin/ # cgi脚本路径
<Directory "/home/www/play/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Require all granted
AddHandler cgi-script .cgi .py .sh # 配置cgi脚本后缀
</Directory>
</IfDefine>
</IfModule>

</VirtualHost>

CGI脚本/home/www/play/cgi-bin/index.py

#!/compiler/miniconda3/bin/python

import json
print ("Content-type:application/json")
print ()
print(json.dumps({
"result":"hello cgi"
}))

访问测试

curl -v http://127.0.0.1/cgi-bin/index.py

< HTTP/1.1 200 OK
< Date: Sun, 29 Aug 2021 03:45:33 GMT
< Server: Apache/2.4.29 (Ubuntu)
< Transfer-Encoding: chunked
< Content-Type: application/json
<
{"result": "hello cgi"}

Web框架

掌握了Web程序整个工作流程,学习Web框架便会轻松很多。框架应该是加速我们实现业务系统的工具,而不是什么难学的技术。

在上面例子中我们,手动处理了很多输入与输出

# 从环境变量获取请求参数
query_string = get_env("QUERY_STRING")

...

# 输出用户数据到stdout
import json
print ("Content-type:application/json")
print ()
print(json.dumps({
"result":user
}))

假设现在有一个框架think,也许代码便写成了这样

from think import controller

@controller
def get_user_info(user_id):
user = db.query("select * from user where id=?",user_id)
return {
"result": user
}

框架帮我们处理了所有对接CGI协议的工作,实际上,目前流行的框架也确实工作在应用网关协议这一层。

总结

画一个点技能的路线图:

Browser  <—    HTTP协议  —>     Web Server         <—        应用网关协议          —>     业务系统(PHP,Python,Java...)
| | | | |
(前端) (网络协议、路由、DNS) (操作系统、网络编程 、框架设计、编程语言特性、数据库、中间件、并行/并发、分布式...)


相关资源