目录
1.WSGI接口
2.新建WSGI服务器
3.使用if管理请求路径
4.使用字典管理请求路径
5.使用装饰器管理请求路径
6.requests模块的介绍
演示代码:
import re
import socket
from multiprocessing import Process
class WSGIServer():
def __init__(self, server, port, root):
self.server = server
self.port = port
self.root = root
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.server, self.port))
self.server_socket.listen(128)
def handle_socket(self, socket):
data = socket.recv(1024).decode('utf-8').splitlines()[0]
file_name = re.match(r'[^/]+(/[^ ]*)', data)[1]
# print(file_name)
if file_name == '/':
file_name = self.root + '/index.html'
else:
file_name = self.root + file_name
try:
file = open(file_name, 'rb')
except IOError:
response_header = 'HTTP/1.1 404 NOT FOUND \r\n'
response_header += '\r\n'
response_body = '========Sorry,file not found======='.encode('utf-8')
else:
response_header = 'HTTP/1.1 200 OK \r\n'
response_header += '\r\n'
response_body = file.read()
finally:
socket.send(response_header.encode('utf-8'))
socket.send(response_body)
def forever_run(self):
while True:
client_socket, client_addr = self.server_socket.accept()
# self.handle_socket(client_socket)
p = Process(target=self.handle_socket, args=(client_socket,))
p.start()
client_socket.close()
if __name__ == '__main__':
ip = '0.0.0.0'
port = 8899
server = WSGIServer(ip, port, './pages')
print('server is running at {}:{}'.format(ip, port))
server.forever_run()
WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return 'Hello, web!
'
上面的application()
函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
environ:一个包含所有HTTP请求信息的dict
对象;
start_response:一个发送HTTP响应的函数。
在application()
函数中,调用:
start_response('200 OK', [('Content-Type', 'text/html')])
就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()
函数。start_response()
函数接收两个参数,一个是HTTP响应码,一个是一组list
表示的HTTP Header,每个Header用一个包含两个str
的tuple
表示。
通常情况下,都应该把Content-Type
头发送给浏览器。其他很多常用的HTTP Header也应该发送。
然后,函数的返回值'
将作为HTTP响应的Body发送给浏览器。Hello, web!
'
有了WSGI,我们关心的就是如何从environ
这个dict
对象拿到HTTP请求信息,然后构造HTML,通过start_response()
发送Header,最后返回Body。
整个application()
函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。
不过,等等,这个application()
函数怎么调用?如果我们自己调用,两个参数environ
和start_response
我们没法提供,返回的str
也没法发给浏览器。
所以application()
函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。但是现在,我们只想尽快测试一下我们编写的application()
函数真的可以把HTML输出到浏览器,所以,要赶紧找一个最简单的WSGI服务器,把我们的Web应用程序跑起来。
好消息是Python内置了一个WSGI服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指该实现完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用。
创建hello.py
文件,用来实现WSGI应用的处理函数。
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
print(environ)
return ['Hello, web!
'.encode('utf-8'),'hello'.encode('utf-8')]
创建server.py
文件,用来启动WSGI服务器,加载appliction
函数
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的application函数:
from hello import application
# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print("Serving HTTP on port 8000...")
# 开始监听HTTP请求:
httpd.serve_forever()
文件结构:
├── server.py ├── utils.py ├── pages └── index.html └── templates └── info.html
utlis.py文件
PAGE_ROOT = './pages'
TEMPLATE_ROOT = './templates'
def load_html(file_name, start_response, root=PAGE_ROOT):
"""
加载HTML文件时调用的方法
:param file_name: 需要加载的HTML文件
:param start_response: 函数,用来设置响应头。如果找到文件,请求头设置为200,否则设置为410
:param root: HTML文件所在的目录。默认PAGE_ROOT表示静态HTML文件,TEMPLATE_ROOT表示的是模板文件
:return: 读取HTML文件成功的话,返回HTML文件内容;读取失败提示资源被删除
"""
file_name = root + file_name
try:
file = open(file_name, 'rb')
except IOError:
start_response('410 GONE', [('Content-Type', "text/html;charset=utf-8")])
return ['资源被删除了'.encode('utf-8')]
else:
start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
content = file.read()
return [content]
def load_template(file_name, start_respone, **kwargs):
"""
加载模板文件
:param file_name: 需要加载的模板文件名
:param start_respone: 函数,用来设置响应头。如果找到文件,请求头设置为200,否则设置为410
:param kwargs: 用来设置模板里的变量
:return: 读取HTML文件成功的话,返回HTML文件内容;读取失败提示资源被删除
"""
content = load_html(file_name, start_respone, root=TEMPLATE_ROOT)
html = content[0].decode('utf-8')
if html.startswith(''):
return [html.format(**kwargs).encode('utf-8')]
else:
return content
service.py文件
from wsgiref.simple_server import make_server
from utils import load_html, load_template
def show_home(start_response):
return load_html('/index.html', start_response)
def show_test(start_response):
start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
return ['我是一段普通的文字'.encode('utf-8')]
def show_info(start_response):
return load_template('/info.html', start_response, name='张三',age=18})
def application(environ, start_response):
path = environ.get('PATH_INFO')
# 处理首页请求(加载一个HTML文件)
if path == '/' or path == '/index.html':
result = show_home(start_response)
return result
# 处理test.html请求(返回一个普通的字符串)
elif path == '/test.html':
return show_test(start_response)
# 处理info.html请求(加载一个模板并且返回)
elif path == '/info.html':
return show_info(start_response)
# 其它请求暂时无法处理,返回404
else:
start_response('400 NOT FOUND', [('Content-Type', "text/html;charset=utf-8")])
return ['页面未找到'.encode('utf-8')]
httpd = make_server('', 8000, application)
print("Serving HTTP on port 8000...")
httpd.serve_forever()
文件结构:
├── server.py ├── utils.py ├── urls.py ├── pages └── index.html └── templates └── info.html
urls.py文件:该文件里只有一个字典对象,用来保存请求路径和处理函数之间的对应关系。
urls = {
'/': 'show_home',
'/index.html': 'show_home',
'/test.html': 'show_test',
'/info.html': 'show_info'
}
server.py文件:
from wsgiref.simple_server import make_server
from urls import urls
from utils import load_html, load_template
def show_home(start_response):
return load_html('/index.html', start_response)
def show_test(start_response):
start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
return ['我是一段普通的文字'.encode('utf-8')]
def show_info(start_response):
return load_template('/info.html', start_response, name='张三',age=18})
def application(environ, start_response):
path = environ.get('PATH_INFO')
# 这里不再是一大堆的if...elif语句了,而是从urls字典里获取到对应的函数
func = urls.get(path)
if func:
return eval(func)(start_response)
# 其它请求暂时无法处理,返回404
else:
start_response('400 NOT FOUND', [('Content-Type', "text/html;charset=utf-8")])
return ['页面未找到'.encode('utf-8')]
httpd = make_server('', 8000, application)
print("Serving HTTP on port 8000...")
httpd.serve_forever()
from wsgiref.simple_server import make_server
from utils import load_html, load_template
g_url_route = {}
def route(url):
def handle_action(action):
g_url_route[url] = action
def do_action(start_response):
return action(start_response)
return do_action
return handle_action
@route('/index.html')
@route('/')
def show_home(start_response):
return load_html('/index.html', start_response)
@route('/test.html')
def show_test(start_response):
start_response('200 OK', [('Content-Type', "text/html;charset=utf-8")])
return ['我是一段普通的文字'.encode('utf-8')]
@route('/info.html')
def show_info(start_response):
return load_template('/info.html', start_response, name='张三', age=18)
def application(environ, start_response):
file_name = environ.get('PATH_INFO')
try:
return g_url_route[file_name](start_response)
except Exception:
start_response('404 NOT FOUND', [('Content-Type', 'text/html;charset=utf-8')])
return ['对不起,界面未找到'.encode('utf-8')]
if __name__ == '__main__':
httpd = make_server('', 8000, application)
print("Serving HTTP on port 8000...")
httpd.serve_forever()
除了使用浏览器给服务器发送请求以外,我们还可以使用第三方模块requests用代码来给服务器发送器请求,并获取结果。
url = 'https://www.apiopen.top/satinApi'
params = {'type': 1, 'page': 2}
response = requests.get(url, params)
print(response)
# 方法二: 只能用于get请求
url = 'https://www.apiopen.top/satinApi?type=1&page=1'
response = requests.get(url)
# print(response)
# 2.获取请求结果
print(response.headers)
# 2)响应体(数据)
# a.获取二进制对应的原数据(数据本身是图片、压缩文件、视频等文件数据)
content = response.content
print(type(content))
# b.获取字符类型的数据
text = response.text
print(type(text))
# c.获取json数据(json转换成python对应的数据)
json = response.json()
print(type(json))
print(json)