import socket
HOST = ''
PORT = 80
listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)
connection, address = listen_socket.accept()
request = connection.recv(1024)
connection.sendall("""HTTP/1.1 200 OK
Content-type: text/html
<h1>Hello, World!</h1>
</body>
</html>""")
connection.close()
(如果上面的代码不能运行,尝试把端口号(PORT)更换为8080
)
这段代码接收连接信号和请求信号,不管请求任何的URL,它都回复HTTP 200
响应(所以这段代码不是一个真正的web服务器)。Content-type: text/html
是头(header)字段,被用来表示请求和响应的元信息(meta-information:元信息可用于浏览器(如何显示内容或重新加载页面),搜索引擎(关键词)参考:meta)。这例子中,我们告诉客户端数据类型是HTML:Content-type: text/html
(而不是JSON:Content-Type: application/json
)。
3.1 剖析Request
web应用接收到了请求,需要分析request,从而作出正确的处理。还需要返回正确的response,所以应用需要能够分析request和生成response,下面就说说什么是:request和response。
如果仔细观察HTTP
请求,你会发现它和响应长的特别像。第一行是<HTTP Method> <URL> <HTTP version>
(HTTP方法,请求的URL,HTTP协议版本),例如:GET / HTTP/1.1
。接下来,就是一些header(头)字段,例如:Accept: */*
(意思是:可以接收任何类型的响应)。这里说的比较笼统,想要深究的同学可以参考:HTTP真的很简单
上面说了Request的格式,下面该说说Response的格式:<HTTP version> <HTTP Status-Code> <Status-Code Reason-Phrase>
(HTTP协议版本,HTTP状态码,HTTP状态码描述),例如:HTTP/1.1 200 OK
。接下来和请求的格式一样,也是头字段,最后就是response包含的实际内容了。内容可以是字符串或者二进制对象(比如:图片和文件就是二进制对象),Content-type
头字段指出了,内容类型,用于定义网络文件的类型,让客户端知道响应回来的数据类型,得到正确的响应结果。
3.2 web服务器,还要做的杂活
如果我们要在上面那个简单的web应用的基础上继续,搭建出一个web应用,我们还需要解决以下几个问题:
如何解析url使得,返回url对应的结果。(路由:route)
如何在支持GET的基础上,支持POST。
如何处理cookie和session。
如何处理上千的并发量。
没有人愿意每次建立web应用的时候都需要去处理这些问题。所以,就出现解决这些问题和HTTP协议的packages(包)。记住:这些包的核心方法我们上面的例子是一样的:监听端口,接收请求,返回HTTP
响应和HTML。
3.3 解决两个大问题:Routing(路由)和Templates(模版)
上面列出的建立web应用的问题,这里挑出两个:
如何把请求的URL对应到处理这个请求的方法上?
如何根据处理完的结果,动态的生成请求的HTML?
每一个web框架解决这两个问题用的都是不同的方式,有许多不同的方法。举例子说明,应该跟有助于理解。所以,下面我们讲讨论:Django和Flask的解决这两个问题的方法。在此之前,我们需要简单的说一下MVC模式。
3.3.1 MVC在Django的应用
Django使用了MVC模式,MVC:“Model-View-Controller”(模型(Model)、视图(View)和控制器(Controller)),把应用中的逻辑分成三个部分。资源,数据库中的表相当于Model
数据层。Controller
控制器包含应用的业务逻辑和操作数据的操作。View
视图,用于展示信息,动态的生成HTML,作为响应结果。
在Django中,控制器(Controller)被叫做视图(Views),视图(Views)被叫做模版(template)。这种叫法是有些奇怪,但是Django是一个简单,优雅的MVC结构的实现。
3.3.2 Routing in Django(Django的路由)
路由是:把请求的URL和处理这个请求的代码,处理结果联系起来。拿咱们上面那个“最简单的web应用例子”来说,所有的请求,都是被同一段代码处理。正常的都是每个URL都有对应一个处理(handler)的函数,比如:我们访问www.foo.com/bar
,它就对应handle_bar()
函数,这个函数负责返回响应。我们可以把应用中的每个URLs都和一个方法一一对应起来。
如果请求的url中带有参数,我们如果获取url中的参数?并把参数传递给对应的处理函数?例如:www.foo.com/users/3/
,我们想要显示user的id:3。
Django的解决办法是,通过正则表达式得到url中的的参数。比如:把url匹配^/users/(?P<id>\d+)/$
的结果传入display_user(id)
函数,这个id
参数就是url匹配出来的结果。通过这种方法,形如:/users/<some number>/
的URL都将调用display_user
函数。
3.3.3 Routing in Flask(Flask的路由)
Flask的路由管理机制和Django的有所不同,它使用的是route()
装饰器。如下:
@app.route('/users/<id:int>/')
def display_user(id):
这如上面所展示的,使用装饰器实现URL和方法的一一对应,更加简洁明了。参数通过<name:type>
格式,专递给修饰的方法,静态的URLs比如:/info/about_us.html
,你可以这样处理:@app.route('/info/about_us.html')
。
3.3.4 模版生成HTML
我们说完如何把‘URL和处理函数一一对应起来并传递参数到函数中’,这次我们需要把URL中的参数展示到页面上,也就是根据请求动态的生成页面(HTML)。Django和Flask都是用HTML模版来决解这种情况。
HTML模版类似于使用str.format()
:它把动态的值(根据请求返回的值)有占位符来代替,最后输出的时候,再把通过str.format()
函数,把占位符转化成具体的值。把一个页面想象成一个很长的字符串,里面的动态变量用占位符来替代,最后调用str.format()
函数。Django和Flask使用的都是jinjia2模版引擎。
模版引擎是为了动态生成页面,所以它的主要作用就是:生成一个模版,用于根据请求返回的值,生成页面,就像‘填空’。例如:模版内容:我是__程序员,而我想要展示:我是python程序员,而你没准想要显示:我是java程序员。具体实现,参考上面给的连接。
4. 数据交互
Django有着‘自带电池’的设计哲学,包含了ORM(对象关系映射:数据库中的表对应成代码中的类),使得操作数据就像操作变得简单。Flask是‘微型框架’,它没有自带数据交互的模版,但是你可以使用SQLAlchemy。Flask和Django通过使用ORM使得数据:增,删,改,查;变得十分方便,简单。
web框架总结
到此为止,web框架到底做了什么就变得很清晰了:把HTTP
的请求和响应,根据特定的规则对应到具体的处理函数上。把处理完的结果,传递到模版引擎上。Django和Flask是两个极端。Django包含了各种功能;Flask是‘微型框架’,只包含web框架的基本功能,但可以通过第三方包来扩充功能。
记住,Python web框架做的都是同样一件事情:接收HTTP
请求,分配处理的方法,生成HTML作为HTTP
响应,返回给客户端。实际上,几乎所有的web框架都是做了这些工作。但愿,你现在能够根据自己的需求,挑选最适合的框架了。