Robyn高性能Web框架系列02:请求-响应过程


Robyn高性能Web框架系列01:Robyn快速入门 一节中,我们介绍了Robyn在设计理念和代码风格上借鉴了FastAPI,在使用上基本可以照搬FastAPI,甚至可以将原来基于FastAPI编写的程序直接转换为Robyn程序,因此对于Robyn的基本用法我们将快速通过,小伙伴们可以参考本号的“FastAPI系列”教程。

Robyn请求

在Robyn中,来自客户的端的HTTP请求信息经过解析后放在了Request 对象中,我们可以在路由函数中通过request参数来访问Request 对象,请详细成员信息如下:

Request
+Url url
+str ip_addr
+str method
+Headers headers
+str | bytes body
+dict<str, str> path_params
+QueryParams query_params
+dict<str, str> form_data
+dict<str, bytes> files
+Identity identity
  • url(Url): 请求的URL地址
  • ip_addr (Optional[str]): 客户端的 IP 地址
  • method(str):请求的HTTP方法。e.g. GET, POST, PUT, DELETE
  • headers(dict[str, str]):请求的标头。e.g. {"Content-Type": "application/json"}
  • body (Union[str, bytes]):请求的主体。如果请求是 JSON,则它将是一个字典。
  • params (dict[str, str]): 请求的参数。e.g. /user/:id -> {"id": "123"}
  • query_params(QueryParams):请求的查询参数。e.g. /user?id=123 -> {"id": [ "123" ]}
  • form_data(dict):请求的表单数据。
  • files(dict):请求的文件数据。
  • 身份(Identity):客户端的身份标签

1、请求的URL、来源IP、HTTP方法

在Request 对象中可以通过request.url、request.ip_addr、request.method的属性获得HTTP请求的基本信息。

from robyn import Robyn, Response  
from robyn.robyn import QueryParams  
from robyn.types import Method, IPAddress, PathParams, FormData, Files

@app.get("/info")  
async def info(request):  
    return { 
        "RequestURL": request.url.path,
        "RequestIP": request.ip_addr,
		"RequestMethod": request.method
    }

2、HTTP headers、cookies

在Request 对象中提供了headers集合,可以通过该集合获得相应的header数据。Robyn没有独立的cookie集合,只能通过headers集合获得cookie值,自行解析。

@app.get("/headers")  
async def headers(request):  
    return {  
        "host": request.headers.get("host"),  
        "user-agent": request.headers.get("user-agent"),  
        "cookie": request.headers.get("cookie")  
    }

3、路径参数、查询参数

Request 对象会根据路由配置进一步解析请求url的信息,提取出路径参数和查询参数分别放置在path_params和query_params中。

@app.get("/params/:foo")  
async def params(request):  
	foo = request.path_params.get('foo', 'default_foo')  
	msg = request.query_params.get('msg', 'default message')
    return {'msg': 'Request params received'}

但是在Robyn中,是无法直接通过参数名 获取路径参数或查询参数的,下列代码不成立:

@app.get("/params/:foo")  
async def params(foo: str):  
    return {'foo': foo}

Robyn 支持使用*extra路由定义中的语法捕获额外的路径参数。这允许你捕获已定义路由之后的 URL 路径中的任何其他段。

@app.get("/params/extra/*extra")
async def param_extra(request: Request):
    extra = request.path_params["extra"]
    return extra

4、请求体、表单与文件

在Request 对象中提供了body属性用以获取请求体数据,如果请求是 JSON,它会被解析成一个字典。Request 对象也提供了form_data和files两个集合,用以访问客户端提交的表单和文件数据。

@app.get("/body")  
async def body(request):  
    body = request.body  
    form = request.form_data  
    files = request.files  
    file_names = list(files.keys())  
    return {'msg': 'Request body received'}

5、请求参数的对象化

除了可以通过Request 对象访问请求数据外,Robyn还允许通过注入到路由函数的类型化对象访问这些数据,如:

@app.get("/obj_param/:foo")  
async def obj_param(req_method:Method, req_ip:IPAddress,path:PathParams,query:QueryParams,form:FormData,files:Files):  
    path_foo = path.get('foo')  
    query_foo = query.get('foo', 'default_query_foo')  
    form_foo = form.get('foo')  
    files_foo = files.get('foo')  
    print(path_foo)  
    print(query_foo)  
    print(form_foo)  
    print(files_foo)  
    return {  
        "RequestIP": req_ip,  
        "RequestMethod": req_method  
    }

总体而言,在请求参数的处理上,Robyn还是不如FastAPI那么丝滑、简洁。

Robyn路由

1、路由规则

Robyn的路由规则比较简单,简单的勉强够用,它主要包括以下路由规则 :

  • 静态路径,如 /ping
  • 动态参数,如 :item_id
  • 通配符路径,如 /*extra

2、路由注册

Robyn提供了通过注解的方式注册路由:

from robyn import Robyn  
  
app = Robyn(__file__)  
  
@app.get("/say_hello") 
def say_hello():  
    return "Hello world!"

也可以通过add_route方法注册路由:

from robyn import Robyn  
  
app = Robyn(__file__)  

def say_hello():  
    return "Hello world!"
    
app.add_route(  
    route_type=HttpMethod.GET,  
    endpoint="/say_hello",  
    handler = say_hello  
)

针对较大的项目, Robyn还提供了SubRouter用于模块化路由管理:

from robyn import Robyn, SubRouter  
app = Robyn(__file__)  
# 声明user子路由
user_router = SubRouter(__name__, prefix="/user")  
@user_router.get("/info")  
async def info():  
    return {  
        "Name": "Robyn",  
        "Age": 20  
    }  
# 注册user子路由
app.include_router(user_router)

Robyn也能为不同的HTTP请求方法如 GET、POST、PUT、PATCH 和 DELETE绑定相同名称的路由函数:

@app.get("/say_hello")  
async def say_hello():  
    return {"message": "Hello From Get!"}  
  
@app.post("/say_hello")  
async def say_hello():  
    return {"message": "Hello From Post!"}

3、同步、异步函数

像FastAPI 一样,Robyn的路由函数也可以是同步函数或异步函数,只不过Robyn的异步函数性能上有着比较的明显提升。

Robyn响应

Robyn同样也提供了一个Response类,用于设置返回客户端的控制状态码、响应头、内容格式、Cookies 和二进制流输出等HTTP响应信息。

Response
+int status_code
+dict<str,str> headers
+str|bytes description
+bytes|Iterable<bytes> body
+set_cookie(key, value, **options)
  • status_code:HTTP 状态码,比如 200、404。
  • headers:响应头集合,键值对形式。
  • description:简单双向输出内容,用于纯文本或字节响应。
  • body:用于二进制或生成器内容,适用于流式输出。
  • set_cookie(…):用于设置 Set-Cookie 的方法。

1、HTTP status、 headers、cookies

以下为基本的Response使用代码:

@app.get("/basic_resp")  
async def basic_resp(request:Request):  
    resp = Response(  
        status_code=202,  
        headers={"X-Note": "Accepted"},  
        description="Send a basic response",  
    )  
    # 设置 Cookie    
    resp.set_cookie("session", "abc123")  
    return resp

以下为向客户端输出二进制文件流的Response代码:

@app.get("/stream")
async def stream(request):
    # 流式输出二进制内容
    return Response(
        status_code=200,
        headers={"Content-Type": "application/octet-stream"},
        body=(f"line {i}\n".encode() for i in range(10))
    )

以下为输出重定向(Redirect)的Response代码:

@app.get("/redirect")
async def index():
    return Response(status_code=307, headers={"Location": "/login"})

2、响应主体

在Robyn中没有类似于FastAPI的JSONResponse、HTMLResponse、PlainTextResponse、FileResponse、StreamingResponse、RedirectResponse等Response派生类型,但它会自动根据路由函数的返回类型判断响应类型,具体规则如下:

  • dict → JSONResponse
  • str / bytes → PlainTextResponse
  • 返回 HTML 字符串 → HTMLResponse
  • 返回 Response(body=…) 生成器或字节流 → StreamResponse

返回一个PlainText响应体

@app.get("/plain")
def plain_resp(request):
    return Response(
        status_code=200,
        headers={"X-Mode": "plain"},
        description="Plain text",
    )

返回一个JSON响应体

@app.get("/json")
async def json_resp():
    return {"msg": "hello", "ok": True}

返回一个Stream响应体

@app.get("/stream")

async def stream_resp():
    return Response(
        body=(b"num:%d\n" % i for i in range(5)),
        headers={"Content-Type": "text/plain"},

    )

返回一个HTML响应体

@app.get("/html")
async def html_resp():
    return "<h1>Hello HTML</h1>"

3、全局header

Robyn中还提供了一种为整个应用输出统一header的方式,从一定程度上减少了路由函数的重复劳动:

app.add_response_header("content-type", "application/json")
app.set_response_header("content-type", "application/json")
app.exclude_response_headers_for(["/login", "/signup"])

Robyn在一个Web框架最为常用的“HTTP请求-响应过程”的实现上仅仅做到了可用的水平上,比起FastAPI这样的成熟框架还是有不少的优化空间的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值