# python-simple-http-server **Repository Path**: dingcx/python-simple-http-server ## Basic Information - **Project Name**: python-simple-http-server - **Description**: 一个超轻量级的 HTTP Server,比 Flask 更轻量级! - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 60 - **Created**: 2020-12-03 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # python-simple-http-server ## 简介 这是一个轻量级的基于 Python http.server 编写的服务器,你可以非常容易的搭建一个 Restful API。其中一些请求的转发等参考了 SpringMVC 的设计。 ## 支持的 Python 的版本 Python 2.7 / 3.6+ (3.5 也应该支持,没有在3.5环境测试过) ## 为什么要选择这个项目? * 轻量级 * 支持过滤器链 * Spring MVC 风格的请求映射配置 * 简单易用 * 编写风格自由 ## 安装 ```shell pip install simple_http_server ``` ## 编写控制器 ### 配置路由信息 我们接下来,将处理请求的函数命名为 **控制器函数(Controller Function)**。 类似 Spring MVC,我们使用描述性的方式来将配置请求的路由(在 Java 中,我们会使用标注 Annotation,而在 Python,我们使用 decorator,两者在使用时非常类似)。 基础的配置如下,该例子中,请求 /index 将会路由到当前的方法中。 ```Python from simple_http_server import request_map @request_map("/index") def your_ctroller_function(): return "
Hello, World!" ``` @request_map 会接收两个参数,第二个参数会指定当前的控制器函数会处理请求中哪些方法(Method),以下的例子中的方法,仅会处理方法为 GET 的请求。 ```Python @request_map("/say_hello", method="GET") def your_ctroller_function(): return "Hello, World!" ``` method参数同时也可以是一个列表,以下的例子中,该控制器函数会处理方法为 GET、POST、PUT 的请求 ```Python @request_map("/say_hello", method=["GET", "POST", "PUT"]) def your_ctroller_function(): return "Hello, World!" ``` 匹配路由时,除了指定具体的路径之外,你还可以指定路径的某一段是可变的,这部方可变的部分将会作为路径参数放入请求中,如何取得这些路径参数我们将会在 **取得请求参数** 一节具体说明。@request_map 的配置如下。该配置下,`/say/hello/to/world`,`/say/hello/to/jhon` 等 url 都能访问该控制器方法。 ```Python @request_map("/say_hello/to/{name}", method=["GET", "POST", "PUT"]) def your_ctroller_function(): return "Hello, World!" ``` 你可以给一个控制器函数增加多个 @request_map ```python @request_map("/") @request_map("/index") def index(): return "Hello, World!" ``` ### 取的请求中的信息 参考了 Spring MVC 的设计,获取请求方法的方式非常自由。其中最基本的方法是取得 Request 对象,该对象中包含了所有其他方式能够获取的内容。 ```Python from simple_http_server import request_map from simple_http_server import Request @request_map("/say_hello/to/{name}", method=["GET", "POST", "PUT"]) def your_ctroller_function(req=Request()): """ 请注意关键字参数传入的默认参数是一个 Request 对象,而不是类本身。 """ ## # 该请求的方法,可能是 "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT" 中的一个 print(req.method) ## # 该请求的路径,就是 /say/hello/to/xxx print(req.path) ## # 一个 dict 对象,包含了所有的头部参数。 print(req.headers) ## # 一个 dict 对象,包含了请求的参数,包括了来源于QueryString与 body 中的参数 # 该对象仅存储一个参数,如果同一个参数名传入了多个值,该对象只记录第一个值。 print(req.parameter) ## # 一个 dict 对象,类似,req.parameter,但该对象包含了所有的参数 # 该对象的值总是会返回一个列表,即使一个参数名只传入了一个参数。 print(req.parameters) ## # 返回 query string print(req.query_string) ## # 返回一个 Cookies 对象,该对象是 http.cookies.SimpleCookie 的子类 print(req.cookies) ## # 一个 dict 对象,包含了所有的路径上的参数,本例中是 {"name": "xxx"} print(req.path_values) ## # 请求体部分,在 3.6 中是一个 bytes 对象。2.7 中是一个 str 对象 print(req.body) ## # 当你的请求的 Content-Type 是 application/json 时,框架会自动将请求体中的 JSON 对象加载为一个dict对象。 print(req.json) return "Hello, World!" ``` 我们还可以通过更直接的参数和关键字参数来获取请求中的信息,使得编码更加简洁和方便。 ```python from simple_http_server import request_map @request_map("/say_hello/to/{name}", method=["GET", "POST", "PUT"]) def your_ctroller_function( user_name, # 传入 req.parameter["user_name"],如果该参数为空,则会响应为 400 参数错误 password, # 传入 req.parameter["password"],如果参数为空,则会响应为 400 参数错误 nickName="", # 传入 req.parameter["nickName"],如果参数为空,则会传入 "" age=16, # 传入 int(req.parameter["age"]),如果传入空则传入 16,如果传入的不是数字类型,会返回 400 参数错误 male=True, # 传入 0, false, 空字符串 为 False,其他均为 True,如果不传入,传入这里指定的默认值 skills=[], # 传入 req.parameters["skills"],会返回一个数组,如果没有传入任何的内容,则返回这里指定的数组 extra={} # 传入 json.loads(req.parameter["extra"]),如果不传入则传入这里指定的 dict 对象,如果传入的字符串不是 JSON 格式,则响应为 400 参数错误 ): return "Hello, World!" ``` 以上的是基础类型的获取,实施上,我们还提供了几个类,通过这些类,你还能快速地获取一些在请求头中,Cookies 中,请求体,路径中的信息。以下是一些代码实例: ```python from simple_http_server import request_map from simple_http_server import Parameter from simple_http_server import Parameters from simple_http_server import Headers from simple_http_server import Header from simple_http_server import Cookies from simple_http_server import Cookie from simple_http_server import PathValue @request_map("/say_hello/to/{name}", method=["GET", "POST", "PUT"]) def your_ctroller_function( user_name=Parameter("userName", required=True), # 传入 req.parameter["userName"],如果该参数为空,则会响应为 400 参数错误 password=Parameter("password", required=True), # 传入 req.parameter["password"],如果参数为空,则会响应为 400 参数错误 nickName=Parameter(default=""), # 传入 req.parameter["nickName"],如果参数为空,则会传入 "",参数名和 skills=Parameters(required=True), # 传入 req.parameters["skills"],会返回一个数组,如果没有传入任何的内容,则响应为 400 参数错误 all_headers=Headers(), # 传入 req.headers user_token=Header(name="userToken", required=True), # 传入 req.headers["userToken"],如果请求头中没有 "userToken" 字段,则响应为 400 参数错误 all_cookies=Cookies(), # 传入 req.cookies,返回所有当前请求的 cookies user_info=Cookie("userInfo", required=False), # 传入 req.cookies["userInfo"],如果没有该 cookie,则响应为 400 参数错误 name=PathValue("name"), # 传入 req.path_values["name"],返回路径中你路由配置中匹配 {name} 的字符串 ): return "Hello, World!" ``` 从上述的例子我们看出,这些类中的参数均有默认值,即使不传入,也能返回正确的数据。除了上述的这些例子之外,我们还有一些额外的情况。例如请求的 Content-Type 是 application/json,然后我们将 JSON 字符串直接写入请求体中,我们可以这样获取信息: ```python from simple_http_server import request_map from simple_http_server import JSONBody @request_map("/from_json_bldy", method=["post", "put", "delete"]) def your_json_body_controller_function(data=JSONBody()): ## # JSONBody 是 dict 的子类,你可以直接其是一个 dict 来使用 print(data["some_key"]) return "Hello, World!" ``` 我们也支持使用 multipart/form-data 上传文件,你可以这样获取文件: ```python from simple_http_server import request_map from simple_http_server import MultipartFile @request_map("/upload", method="POST") def upload( img=MultipartFile("img", required=True) # 如果没有传入 img 参数,或者该参数不是一个文件,均响应为 400 参数错误 ): root = os.path.dirname(os.path.abspath(__file__)) ## # 获取上传文件的 content-type print(img.content_type) ## # MultipartFile.content 在 3.6 中为 bytes 类型,在 2.7 中为字符串 print(img.content) ## # 还可以通过内置的 save_to_file 将内容直接写入到某个文件中 img.save_to_file(root + "/uploads/imgs/" + img.filename) return "upload ok!" ``` ### 响应请求 从上述的例子中可以看出,取得请求中的参数我们有许多方式,这个给了开发者很高的自由度来编写这些信息。而响应的方法一样具有各种方法。 上述的例子中是其中一种,我们直接返回了一个HTML格式的字符串,该框架会自动的将这个字符串响应为 text/html 格式。 除了 HTML5 格式的字符串,我们还可以返回以下的内容: ```python ## # 如果你返回一个 dict,那么框架将会将其响应为 application/json return {"success": True, "message": "Success!"} ``` ```python ## # 你可以返回一个 XML 格式的字符串,框架会将其响应为 text/xml return "