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 日

#4HTTP 服务器

Download history 29790/week @ 2024-05-04 31372/week @ 2024-05-11 32387/week @ 2024-05-18 31503/week @ 2024-05-25 34532/week @ 2024-06-01 31102/week @ 2024-06-08 33382/week @ 2024-06-15 35782/week @ 2024-06-22 29985/week @ 2024-06-29 24883/week @ 2024-07-06 21741/week @ 2024-07-13 24525/week @ 2024-07-20 25881/week @ 2024-07-27 23605/week @ 2024-08-03 23893/week @ 2024-08-10 21121/week @ 2024-08-17

每月 98,382 次下载
用于 25 crate(22 个直接使用)

Apache-2.0

630KB
13K SLoC

Rust 中的 lambda-http 用于 AWS Lambda

Docs

lambda-http 是一个抽象层,它从不同的服务中获取有效载荷并将它们转换为 http 对象,使得在 Rust 中编写针对 API Gateway 代理事件的重点 Lambda 函数变得简单。

lambda-http 处理器由以下部分组成

我们能够处理来自以下服务的请求

多亏了 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::RequestPayloadExtlambda_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可用的功能标志如下

如果您只想支持这些来源之一,您可以在您的包的Cargo.toml文件中禁用默认功能,并只启用您关心的来源。将lambda_http的依赖项行替换为以下片段,更改您想要启用的功能

[dependencies.lambda_http]
version = "0.5.3"
default-features = false
features = ["apigw_rest"]

依赖项

~11–21MB
~372K SLoC