测试环境
win11专业版
python 3.9
代码实现
# -*- coding:utf-8 -*-
import json
import traceback
import uuid
from http.server import HTTPServer, ThreadingHTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
class MyHTTPRequestHandler(BaseHTTPRequestHandler):
def _send_response(self, status_code, content_type, body):
'''发送响应信息'''
self.send_response(status_code)
self.send_header('Content-type', content_type)
self.send_header('Cache-Control', 'no-store') # 防止缓存旧编码响应
self.end_headers()
self.wfile.write(body.encode('utf-8'))
def _handle_home(self):
'''访问主页请求处理'''
html = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><h1>Home Page</h1>'
self._send_response(200, 'text/html; charset=utf-8', html)
def _handle_404(self):
'''请求不存在资源处理'''
# json_respose = {"success": False, "message": "Not Found"}
# self._send_response(404, 'application/json;charset=utf-8', json.dumps(json_respose))
# Content-Type 指定 charset=utf-8 可避免浏览器GET请求,界面中文显示乱码问题
self._send_response(404, 'text/html; charset=utf-8', "<h1>404 Not Found</h1>")
def _handle_login(self, login_req_data):
'''处理登录请求'''
try:
data = json.loads(login_req_data)
username = data.get('username')
password = data.get('password')
ip = data.get('ip')
response = {
'code': 0,
'token': uuid.uuid4().hex,
'description': 'success'
}
self._send_response(200, 'application/json; charset=utf-8', json.dumps(response))
except Exception as e:
error_msg = traceback.format_exc()
print(error_msg)
response = {
'code': 1,
'token': '',
'description': error_msg
}
self._send_response(500, 'application/json; charset=utf-8', json.dumps(response))
def do_GET(self):
'''处理GET请求'''
parsed_path = urlparse(self.path)
path = parsed_path.path
query_params = parse_qs(parsed_path.query) # 获取URL携带的查询参数
# print('收到GET请求参数:', query_params)
if path == '/':
self._handle_home()
else:
self._handle_404()
def do_POST(self):
'''处理POST请求'''
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
parsed_path = urlparse(self.path)
path = parsed_path.path
query_params = parse_qs(parsed_path.query) # 获取URL 查询参数
# print("收到POST数据:", post_data.decode())
# 路由匹配逻辑
if path == '/':
self._handle_home()
elif path == '/north/login':
self._handle_login(post_data.decode())
else:
self._handle_404()
if __name__ == '__main__':
# server = HTTPServer(('0.0.0.0', 8000), MyHandler) # 阻塞式运行
server = ThreadingHTTPServer(('localhost', 8000), MyHTTPRequestHandler)
print('正在启动服务,访问地址:http://localhost:8000')
server.serve_forever()
扩展:如果我们希望服务在处理请求的时,调用其它类实例的方法,或者更新其它类实例的属性,咋处理呢?
答案:将其它类实例初始化为RequestHandler
的类属性,然后在相关请求处理函数中进行调用
示例
class Subsystem():
def __init__(self, http_server_port):
self.http_server_port = http_server_port
self.server = ThreadingHTTPServer(('0.0.0.0', self.http_server_port), lambda *args: MyHTTPRequestHandler(self, *args))
self.north_server_info = {}
def start_http_server(self):
self.server.serve_forever()
def push_data(self, data):
'''测试函数'''
logger.info(f'pushing data to server')
def update_north_server_info(self, data):
'''测试函数'''
self.north_server_info[data.get('ip')] = data
class MyHTTPRequestHandler(BaseHTTPRequestHandler):
def __init__(self, subsystem, *args, **kwargs):
self.subsystem = subsystem # 保存SubSystem实例引用
super().__init__(*args, **kwargs)
# ....略
def _handle_login(self, login_req_data):
'''处理登录请求'''
try:
data = json.loads(login_req_data)
username = data.get('username')
password = data.get('password')
ip = data.get('ip')
response = {
'code': 0,
'token': uuid.uuid4().hex,
'description': 'success'
}
self.subsystem.push_data('')
self.subsystem.update_north_server_info({'username': username, 'password': password, 'ip': ip})
self._send_response(200, 'application/json; charset=utf-8', json.dumps(response))
except Exception as e:
error_msg = traceback.format_exc()
logger.error(error_msg)
response = {
'code': 1,
'token':'',
'description': error_msg
}
self._send_response(500, 'application/json; charset=utf-8', json.dumps(response))
# 测试
if __name__ == '__main__':
http_port = 8000
for i in range(2):
system = Subsystem(http_port)
thread = threading.Thread(target=system.start_http_server)
thread.start()
http_port += 1
相关介绍
模块简介
http.server
模块定义了用于实现HTTP服务器(Web服务器)的类。
其中一个类,HTTPServer
是socketserver.TCPServer
子类。它创建并监听HTTP套接字,将请求分派给处理程序。创建和运行服务器的代码示例如下:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
类及相关函数简介
-
class http.server.HTTPServer(server_address, RequestHandlerClass)
该类基于基于
TCPServer
类构建,通过将将server地址存储为名为server_name
和server_port
的实例变量。通过handler访问服务,通常是通过handler的server
实例变量 -
class http.server.ThreadingHTTPServer(server_address, RequestHandlerClass)
同
HTTPServer
一样,除了通过使用ThreadingMixIn
使用线程处理请求。 3.7版本中新增
HTTPServer
和ThreadingHTTPServer
必须在实例化时给它一个RequestHandlerClass,此模块提供了三种不同的变体:
-
class http.server.BaseHTTPRequestHandler(request, client_address, server)
此类用于处理到达服务器的HTTP请求。就其本身而言,它无法响应任何实际的HTTP请求;它必须被子类化以处理每个请求方法(例如
GET
或POST
)。BaseHTTPRequestHandler
提供了许多类和实例变量以及子类使用的方法。BaseHTTPRequestHandler
实例变量:-
client_address
包含一个
(host, port)
形式,表示客户端地址的元组 -
server
包含服务器实例
-
close_connection
handle_one_request()
返回前需要设置的布尔值。指示是否可能需要另一个请求,或者是否应该关闭连接。 -
requestline
包含HTTP请求行的字符串表示,不含终止
CRLF
。此属性应通过[handle_one_request()
设置。如果没有处理有效的请求行,则应将其设置为空字符串。 -
command
包含命令(请求类型)。例如,
'GET'
. -
path
包含请求路径。 如果URL的查询参数部分存在,则
path
包括查询参数。 -
request_version
包含请求版本字符串,形如’HTTP/1.0’`。
-
headers
保存由
MessageClass
类变量指定的类的实例。此实例解析和管理HTTP请求中的请求头。parse_headers()
函数来自http.client
,用于解析标头,它要求http请求提供有效的RFC 2822样式请求头。 -
rfile
一个
io.BufferedIOBase
输入流,准备从可选输入数据的开头读取。 -
wfile
包含用于将响应写回客户端的输出流。在写入此流时,必须正确遵守HTTP协议,以实现与HTTP客户端的成功交互操作。在3.6版本中更改:这是一个
io.BufferedIOBase
流。
BaseHTTPRequestHandler
属性:-
server_version
指定服务器软件版本。你可能希望覆盖此内容。格式是多个空格分隔的字符串,其中每个字符串的格式为
name[/version]
。例如,'BaseHTTP/0.2'
。 -
sys_version
包含Python系统版本,其形式和
version_string
方法及server_version
]类变量使用的相同。例如,'Python/1.4'
。 -
error_message_format
指定
send_error()
应使用的格式字符串,用于构建对客户端的错误响应的。默认情况下,基于传递给send_error
的状态代码,用responds
中的变量填充该字符串。 -
error_content_type
指定发送到客户端的错误响应的
Content-Type
HTTP请求头。默认值为'text/html'
。 -
protocol_version
这指定了响应中使用的HTTP协议版本。如果设置为
'HTTP/1.1'
,服务器将允许HTTP持久连接;但是,服务器在对客户端的所有响应中必须包含一个准确的Content-Length
请求头(使用send_header()
)。为了向后兼容,设置默认为“HTTP/1.0”。 -
MessageClass
指定一个
email.message.Message
之类的类,用于解析HTTP头。通常,这是不可覆盖的,默认为http.client.HTTPMessage
。 -
responses
此属性包含整数错误代码到包含短消息和长消息的两个元素元组的映射。例如,
{code: (shortmessage, longmessage)}
。*shortmessage*
通常用作错误响应中的message
键,longmessage
用作explain
键。供send_response_only
和send_error()
方法使用。
BaseHTTPRequestHandler
实例方法:-
handle
()调用
handle_one_request()
实现适当的do_*()
方法。 -
handle_one_request
()此方法将解析请求并将其分发到适当的
do_*()
方法。你应永远不需要重写它。 -
handle_expect_100
()当符合HTTP/1.1标准的服务器收到
Expect: 100-continue
请求头时,它会以100 Continue
和“200 OK
头作为响应。如果服务器不希望客户端继续,则可以重写此方法以引发错误。例如,服务器可以选择发送417 Expectation Failed
作为响应头,并返回False
。3.2版本中的新功能
-
send_error
(code, message=None, explain=None)向客户端发送并记录完整的错误回复。数字code指定HTTP错误代码,message作为可选的、简短的、人类可读的错误描述。explain参数可用于提供有关错误的更详细信息;它将使用
error_message_format
属性进行格式化,并在一组完整的标头之后作为响应体发出。response属性包含message和explain的默认值,如果没有提供值,将使用这些值;对于未知代码,两者的默认值都是字符串???
。如果方法是HEAD
或响应代码是以下代码之一:1xx
,204 No Content
,205 Reset Content
,304 Not Modified
,则请求体将为空。在版本3.4中变更:错误响应包含
Content-Length
头。添加了explain参数。 -
send_response
(code, message=None)添加一个响应头添加到headers缓冲区,并记录接受的请求。HTTP响应行被写入内部缓冲区,后面跟随Server和Date响应头。这两个响应头的值分别从
version_string()
和date_time_string()
方法中获取。如果服务器不打算使用send_header()
发送任何其他请求头,则send_response()
后面应该跟一个end_headers()
调用。3.3版本中变更:header存储在内部缓冲区,且需要显示调用
end_headers()
。 -
send_header
(keyword, value)将HTTP头添加到内部缓冲区,当
end_headers()
或者flush_headers()
被调用时,该缓冲区中的内容将被写入到输出流。keyword应指定header,value指定其值。请注意,在send_headers
调用完成后,必须调用end_headers()
才能完成操作。版本3.2中变更:HTTP头存储在内部缓冲区中
-
send_response_only
(code, message=None)仅发送响应头,服务器向客户端发送
100 Continue
响应时使用。响应头未缓冲,直接发送到输出流。如果未指定message,则发送与响应code对应的HTTP消息。版本3.2中的新功能
-
end_headers
()在header缓冲区中添加一个空行(表示响应中HTTP头的结束)并调用
flush_headers()
。版本3.2中变更:缓冲的HTTP头被写入输出流
-
flush_headers
()最后将HTTP头发送到输出流并刷新内部HTTP头缓冲区。3.3版本中的新功能。
-
log_request
(code=’-‘, size=’-‘)记录已接受(成功)的请求。code应指定与响应关联的数字HTTP code。如果可获取响应的大小,则应将其作为size参数传递。
-
log_error
(…)当请求无法满足时记录错误。默认情况下,它将消息传递给
log_message()
,因此它采用相同的参数(format和其它参数)。 -
log_message
(format, …)将任意消息记录到
sys.stderr
。这通常会被重写以创建自定义错误日志记录机制。format参数是一个标准的类printf风格的格式字符串,其中log_message()
的附加参数作为格式化的输入。客户端ip地址和当前日期和时间都会作为记录的每条消息的前缀。 -
version_string
()返回服务器软件的版本字符串。这是
server_version
以及sys_version
属性的混合。 -
date_time_string
(timestamp=None)返回timestamp给出的日期和时间(必须为
None
或采用time.time()
返回的格式),格式化为消息头。如果省略timestamp,则使用当前日期和时间。结果看起来像'Sun, 06 Nov 1994 08:49:37 GMT'
。 -
log_date_time_string
()返回当前日期和时间,格式化为日志记录格式。
-
address_string
()返回客户端地址。
3.3版本中变更:以前执行了名称查找。为了避免名称解析延迟,它现在总是返回IP地址。
-
-
class http.server.SimpleHTTPRequestHandler(request, client_address, server, directory=None)
介绍略
-
class http.server.``CGIHTTPRequestHandler(*request*, client_address, server)
介绍略