28 个版本 (11 个重大更新)
0.13.0 | 2024 年 7 月 25 日 |
---|---|
0.11.4 | 2024 年 6 月 14 日 |
0.11.1 | 2024 年 3 月 30 日 |
0.9.0 | 2023 年 12 月 20 日 |
0.1.0 | 2019 年 1 月 25 日 |
#4 在 HTTP 服务器 中
每月 98,382 次下载
用于 25 个 crate(22 个直接使用)
630KB
13K SLoC
Rust 中的 lambda-http 用于 AWS Lambda
lambda-http
是一个抽象层,它从不同的服务中获取有效载荷并将它们转换为 http 对象,使得在 Rust 中编写针对 API Gateway 代理事件的重点 Lambda 函数变得简单。
lambda-http 处理器由以下部分组成
Request
- 代表一个 HTTP 请求IntoResponse
- 将IntoResponse
转换为实际的LambdaResponse
的 Future
我们能够处理来自以下服务的请求
- API Gateway REST、HTTP 和 WebSocket API Lambda 集成
- AWS ALB
- AWS Lambda 函数 URL
多亏了 Request
类型,我们能够无缝处理代理集成,无需担心指定具体的服务类型。
还有一个扩展,为 lambda_http::Request
结构体提供对 API 网关 和 ALB 功能的访问。
例如一些实用的扩展
query_string_parameters
- 返回预解析的HTTP查询字符串参数,与请求关联的URL中“?”部分之后提供的参数path_parameters
- 返回预提取的路径参数,在URL占位符/foo/{bar}/baz/{qux}
中提供的参数,与请求相关联lambda_context
- 返回Lambda调用的上下文;请参阅运行时文档request_context
- 返回ALB/API网关请求上下文- payload - 返回将有效载荷解析为实现了
serde::Deserialize
的类型的结果
有关更多扩展,请参阅lambda_http::RequestPayloadExt
和lambda_http::RequestExt
特性
示例
在这里,您将找到一些示例,用于处理基本场景
- 从正文读取JSON并将其反序列化为结构
- 读取查询字符串参数
- Lambda请求授权者
- 将Lambda执行上下文初始化传递给处理程序
从正文读取JSON并将其反序列化为结构
以下代码创建了一个简单的API网关代理(HTTP、REST),它接受输入一个JSON有效载荷。
use lambda_http::{run, http::{StatusCode, Response}, service_fn, Error, IntoResponse, Request, RequestPayloadExt};
use serde::{Deserialize, Serialize};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.without_time()
.with_max_level(tracing::Level::INFO)
.init();
run(service_fn(function_handler)).await
}
pub async fn function_handler(event: Request) -> Result<impl IntoResponse, Error> {
let body = event.payload::<MyPayload>()?;
let response = Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "application/json")
.body(json!({
"message": "Hello World",
"payload": body,
}).to_string())
.map_err(Box::new)?;
Ok(response)
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct MyPayload {
pub prop1: String,
pub prop2: String,
}
读取查询字符串参数
use lambda_http::{run, http::{StatusCode, Response}, service_fn, Error, RequestExt, IntoResponse, Request};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.without_time()
.with_max_level(tracing::Level::INFO)
.init();
run(service_fn(function_handler)).await
}
pub async fn function_handler(event: Request) -> Result<impl IntoResponse, Error> {
let name = event.query_string_parameters_ref()
.and_then(|params| params.first("name"))
.unwrap_or_else(|| "stranger")
.to_string();
// Represents an HTTP response
let response = Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "application/json")
.body(json!({
"message": format!("Hello, {}!", name),
}).to_string())
.map_err(Box::new)?;
Ok(response)
}
Lambda请求授权者
由于lambda-http
是一个抽象层,我们无法将其用于Lambda请求授权者案例。如果您移除了抽象层,您需要处理您服务的请求/响应。
use aws_lambda_events::apigw::{
ApiGatewayCustomAuthorizerRequestTypeRequest, ApiGatewayCustomAuthorizerResponse, ApiGatewayCustomAuthorizerPolicy, IamPolicyStatement,
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.without_time()
.with_max_level(tracing::Level::INFO)
.init();
run(service_fn(function_handler)).await
}
pub async fn function_handler(event: LambdaEvent<ApiGatewayCustomAuthorizerRequestTypeRequest>) -> Result<ApiGatewayCustomAuthorizerResponse, Error> {
// do something with the event payload
let method_arn = event.payload.method_arn.unwrap();
// for example we could use the authorization header
if let Some(token) = event.payload.headers.get("authorization") {
// do something
return Ok(custom_authorizer_response(
"ALLOW",
"some_principal",
&method_arn,
));
}
Ok(custom_authorizer_response(
&"DENY".to_string(),
"",
&method_arn))
}
pub fn custom_authorizer_response(effect: &str, principal: &str, method_arn: &str) -> ApiGatewayCustomAuthorizerResponse {
let stmt = IamPolicyStatement {
action: vec!["execute-api:Invoke".to_string()],
resource: vec![method_arn.to_owned()],
effect: Some(effect.to_owned()),
};
let policy = ApiGatewayCustomAuthorizerPolicy {
version: Some("2012-10-17".to_string()),
statement: vec![stmt],
};
ApiGatewayCustomAuthorizerResponse {
principal_id: Some(principal.to_owned()),
policy_document: policy,
context: json!({ "email": principal }), // https://github.com/awslabs/aws-lambda-rust-runtime/discussions/548
usage_identifier_key: None,
}
}
将Lambda执行上下文初始化传递给处理程序
一种最佳实践是利用执行环境重用,以提高您函数的性能。在函数处理程序之外初始化SDK客户端和数据库连接。由相同函数实例处理的后续调用可以重用这些资源。这通过减少函数运行时间来节省成本。
use aws_sdk_dynamodb::model::AttributeValue;
use chrono::Utc;
use lambda_http::{run, http::{StatusCode, Response}, service_fn, Error, RequestExt, IntoResponse, Request};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.without_time()
.with_max_level(tracing::Level::INFO)
.init();
let config = aws_config::from_env()
.load()
.await;
let dynamodb_client = aws_sdk_dynamodb::Client::new(&config);
run(service_fn(|event: Request| function_handler(&dynamodb_client, event))).await
}
pub async fn function_handler(dynamodb_client: &aws_sdk_dynamodb::Client, event: Request) -> Result<impl IntoResponse, Error> {
let table = std::env::var("TABLE_NAME").expect("TABLE_NAME must be set");
let name = event.query_string_parameters_ref()
.and_then(|params| params.first("name"))
.unwrap_or_else(|| "stranger")
.to_string();
dynamodb_client
.put_item()
.table_name(table)
.item("ID", AttributeValue::S(Utc::now().timestamp().to_string()))
.item("name", AttributeValue::S(name.to_owned()))
.send()
.await?;
// Represents an HTTP response
let response = Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "application/json")
.body(json!({
"message": format!("Hello, {}!", name),
}).to_string())
.map_err(Box::new)?;
Ok(response)
}
与API网关阶段集成
当您将HTTP Lambda函数与API网关阶段集成时,请求中接收到的路径将包含阶段作为第一个段,例如/production/api/v1
,其中production
是API网关阶段。
如果您不希望路径中包含阶段,可以将环境变量AWS_LAMBDA_HTTP_IGNORE_STAGE_IN_PATH
设置为true
,无论是在您的Lambda函数配置中,还是在main
Rust函数内部。在先前的示例中,当此环境变量存在时,函数接收到的路径是/api/v1
,消除了第一个段中的阶段。
功能标志
lambda_http
是来自三个不同服务的HTTP事件的包装器,分别是亚马逊负载均衡器(ALB)、亚马逊API网关(APIGW)和AWS Lambda函数URL。亚马逊API网关还可以从三个不同的端点发送事件,即REST API、HTTP API和WebSockets。lambda_http
将所有这些来源的事件转换为原生http::Request
对象,因此您可以将Rust HTTP语义集成到Lambda函数中。
默认情况下,lambda_http
将您的函数编译为支持这些服务中的任何一个。这增加了函数的编译时间,因为我们必须为所有来源生成代码。实际上,您通常只会将Lambda函数放置在其中一个来源后面。您可以通过功能标志选择为哪个来源生成代码。
lambda_http
可用的功能标志如下
alb
:用于来自Amazon Elastic Load Balancer的事件。apigw_rest
:用于来自Amazon API Gateway Rest API的事件。apigw_http
:用于来自Amazon API Gateway HTTP API和AWS Lambda Function URLs的事件。apigw_websockets
:用于来自Amazon API Gateway WebSockets的事件。
如果您只想支持这些来源之一,您可以在您的包的Cargo.toml
文件中禁用默认功能,并只启用您关心的来源。将lambda_http
的依赖项行替换为以下片段,更改您想要启用的功能
[dependencies.lambda_http]
version = "0.5.3"
default-features = false
features = ["apigw_rest"]
依赖项
~11–21MB
~372K SLoC