英文:
how to create a generic type for lambda middleware in go
问题
我正在使用AWS Lambda中的Go语言,并且正在寻找一个通用的中间件解决方案。我有以下代码:
func WsHandler(ctx context.Context, event events.APIGatewayWebsocketProxyRequest) (events.APIGatewayProxyResponse, error) {
}
type HandlerFunc func(context.Context, events.APIGatewayWebsocketProxyRequest) (events.APIGatewayProxyResponse, error)
func LogMiddleware(next HandlerFunc) HandlerFunc {
return HandlerFunc(func(ctx context.Context, event events.APIGatewayWebsocketProxyRequest) (events.APIGatewayProxyResponse, error) {
return next(ctx, event)
})
}
lambda.Start(LogMiddleware(WsHandler))
中间件函数有一个参数events.APIGatewayWebsocketProxyRequest
,因为目标处理程序WsHandler
使用了这个类型。
我有另一个处理程序,它接受参数event events.APIGatewayProxyRequest
,如下所示。它无法使用这个中间件,因为参数不匹配。
GraphqlQueryMutationHandler(ctx context.Context, event events.APIGatewayProxyRequest){
...
}
我尝试将中间件处理函数更改为interface{}
,但它不起作用。Go语言会报错,指出这个类型不正确。
type HandlerFunc func(context.Context, interface{}) (interface{}, error)
有没有办法使中间件适用于任何处理程序类型?
英文:
I am using go in AWS lambda and looking for a generic middleware solution. I have below code:
func WsHandler(ctx context.Context, event events.APIGatewayWebsocketProxyRequest) (events.APIGatewayProxyResponse, error) {
}
type HandlerFunc func(context.Context, events.APIGatewayWebsocketProxyRequest) (events.APIGatewayProxyResponse, error)
func LogMiddleware(next HandlerFunc) HandlerFunc {
return HandlerFunc(func(ctx context.Context, event events.APIGatewayWebsocketProxyRequest) (events.APIGatewayProxyResponse, error) {
return next(ctx, event)
})
}
lambda.Start(LogMiddleware(WsHandler))
The middleware function has a parameter events.APIGatewayWebsocketProxyRequest
because the target handler WsHandler
uses this type.
I have another handler which takes parameter event events.APIGatewayProxyRequest
as below. It can't use this middleware since the parameter doesn't match.
GraphqlQueryMutationHandler(ctx context.Context, event events.APIGatewayProxyRequest){
...
}
I have tried to change the middleware handle to interface{}
, but it doesn't work. go complains this type.
type HandlerFunc func(context.Context, interface{}) (interface{}, error)
Is there any way to make the middleware works for any handler type?
答案1
得分: 1
让我分享一下我在我的系统上能够复制的工作解决方案。首先,我将与您分享我使用的项目布局:
events/
http_event.json
sqs_event.json
hello-world/
main.go
sqs/
main.go
middlewares/
middlewares.go
现在,让我们关注代码部分。
middlewares/middlewares.go
代码如下:
package middlewares
import (
"context"
"fmt"
"github.com/aws/aws-lambda-go/events"
)
type Record struct {
events.APIGatewayProxyRequest `json:",omitempty"`
events.SQSEvent `json:",omitempty"`
}
type Event struct {
Records []Record `json:"records"`
}
type HandlerFunc func(ctx context.Context, event Event) (string, error)
func LogMiddleware(ctx context.Context, next HandlerFunc) HandlerFunc {
return HandlerFunc(func(ctx context.Context, event Event) (string, error) {
fmt.Println("log from middleware!")
return next(ctx, event)
})
}
让我们总结一下基本概念:
- 我们定义了
Event
结构体,它将是我们的通用事件。它是Record
结构体的包装器。 Record
结构体使用结构体嵌入来嵌入我们要处理的所有事件(例如event.APIGatewayProxyRequest
和SQSEvent
)。- 我们在中间件的签名中依赖于这一点,以使其尽可能通用。
events/http_event.json
{
"records": [
{
"body": "{\"message\": \"hello world\"}",
"resource": "/hello",
"path": "/hello",
"httpMethod": "GET",
"isBase64Encoded": false,
"queryStringParameters": {
"foo": "bar"
},
"pathParameters": {
"proxy": "/path/to/resource"
},
"stageVariables": {
"baz": "qux"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "max-age=0",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Custom User Agent String",
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"requestContext": {
"accountId": "123456789012",
"resourceId": "123456",
"stage": "prod",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"requestTime": "09/Apr/2015:12:34:56 +0000",
"requestTimeEpoch": 1428582896000,
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"accessKey": null,
"sourceIp": "127.0.0.1",
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "Custom User Agent String",
"user": null
},
"path": "/prod/hello",
"resourcePath": "/hello",
"httpMethod": "GET",
"apiId": "1234567890",
"protocol": "HTTP/1.1"
}
}
]
}
这里没有什么重要的要说的。
events/sqs_event.json
{
"records": [
{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": "My own event payload!",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1523232000000",
"SenderId": "123456789012",
"ApproximateFirstReceiveTimestamp": "1523232000001"
},
"messageAttributes": {},
"md5OfBody": "4d1d0024b51659ad8c3725f9ba7e2471",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue",
"awsRegion": "us-east-1"
}
]
}
]
}
这里也是一样的。
hello-world/main.go
package main
import (
"context"
"fmt"
"httplambda/middlewares"
"github.com/aws/aws-lambda-go/lambda"
)
func lambdaHandler(ctx context.Context, event middlewares.Event) (string, error) {
_ = ctx
fmt.Println("Path:", event.Records[0].APIGatewayProxyRequest.Path)
fmt.Println("Hi from HTTP-triggered lambda!")
return "", nil
}
func main() {
// start the lambda handler
lambda.Start(middlewares.LogMiddleware(context.Background(), lambdaHandler))
}
请注意我们如何访问事件的信息。
sqs/main.go
package main
import (
"context"
"fmt"
"httplambda/middlewares"
"github.com/aws/aws-lambda-go/lambda"
)
func lambdaHandler(ctx context.Context, event middlewares.Event) (string, error) {
_ = ctx
fmt.Println("Queue name:", event.Records[0].SQSEvent.Records[0].EventSourceARN)
fmt.Println("Hi from SQS-triggered lambda!")
return "", nil
}
func main() {
lambda.Start(middlewares.LogMiddleware(context.Background(), lambdaHandler))
}
最后
有几点需要考虑:
- 在采用这个解决方案之前,我尝试使用类型参数,但没有成功。似乎在中间件的签名中不允许使用类型参数。
- 代码过于简化,不适合生产环境。
如果这对您有帮助,或者您需要其他任何帮助,请告诉我,谢谢!
英文:
let me share the working solution I was able to replicate on my system. First, I'm going to share with you the project layout I used:
events/
http_event.json
sqs_event.json
hello-world/
main.go
sqs/
main.go
middlewares/
middlewares.go
Now, let's focus on the code.
middlewares/middlewares.go
The code is as follows:
package middlewares
import (
"context"
"fmt"
"github.com/aws/aws-lambda-go/events"
)
type Record struct {
events.APIGatewayProxyRequest `json:",omitempty"`
events.SQSEvent `json:",omitempty"`
}
type Event struct {
Records []Record `json:"records"`
}
type HandlerFunc func(ctx context.Context, event Event) (string, error)
func LogMiddleware(ctx context.Context, next HandlerFunc) HandlerFunc {
return HandlerFunc(func(ctx context.Context, event Event) (string, error) {
fmt.Println("log from middleware!")
return next(ctx, event)
})
}
Let's summarize the fundamental concepts:
- We define the
Event
struct that will be our generic event. It's a wrapper around theRecord
struct. - The
Record
struct uses the struct embedding to embed all of the events that we're going to handle (e.g.event.APIGatewayProxyRequest
andSQSEvent
). - We rely on this in the middleware's signature to be as much generic as we can.
events/http_event.json
{
"records": [
{
"body": "{\"message\": \"hello world\"}",
"resource": "/hello",
"path": "/hello",
"httpMethod": "GET",
"isBase64Encoded": false,
"queryStringParameters": {
"foo": "bar"
},
"pathParameters": {
"proxy": "/path/to/resource"
},
"stageVariables": {
"baz": "qux"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "max-age=0",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Custom User Agent String",
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"requestContext": {
"accountId": "123456789012",
"resourceId": "123456",
"stage": "prod",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"requestTime": "09/Apr/2015:12:34:56 +0000",
"requestTimeEpoch": 1428582896000,
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"accessKey": null,
"sourceIp": "127.0.0.1",
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "Custom User Agent String",
"user": null
},
"path": "/prod/hello",
"resourcePath": "/hello",
"httpMethod": "GET",
"apiId": "1234567890",
"protocol": "HTTP/1.1"
}
}
]
}
Here nothing relevant to say.
events/sqs_event.json
{
"records": [
{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": "My own event payload!",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1523232000000",
"SenderId": "123456789012",
"ApproximateFirstReceiveTimestamp": "1523232000001"
},
"messageAttributes": {},
"md5OfBody": "4d1d0024b51659ad8c3725f9ba7e2471",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue",
"awsRegion": "us-east-1"
}
]
}
]
}
The same applies here.
hello-world/main.go
package main
import (
"context"
"fmt"
"httplambda/middlewares"
"github.com/aws/aws-lambda-go/lambda"
)
func lambdaHandler(ctx context.Context, event middlewares.Event) (string, error) {
_ = ctx
fmt.Println("Path:", event.Records[0].APIGatewayProxyRequest.Path)
fmt.Println("Hi from HTTP-triggered lambda!")
return "", nil
}
func main() {
// start the lambda handler
lambda.Start(middlewares.LogMiddleware(context.Background(), lambdaHandler))
}
Please note how we can access the info on the event.
sqs/main.go
package main
import (
"context"
"fmt"
"httplambda/middlewares"
"github.com/aws/aws-lambda-go/lambda"
)
func lambdaHandler(ctx context.Context, event middlewares.Event) (string, error) {
_ = ctx
fmt.Println("Queue name:", event.Records[0].SQSEvent.Records[0].EventSourceARN)
fmt.Println("Hi from SQS-triggered lambda!")
return "", nil
}
func main() {
lambda.Start(middlewares.LogMiddleware(context.Background(), lambdaHandler))
}
Final
There are a couple of considerations to do:
- Before following this solution, I tried to use the type parameters without any luck. It seems that they're not allowed in the middlewares' signatures.
- The code is oversimplified and it's not production-ready.
Let me know if this helps or if you need anything else, thanks!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论