[Go] Lambda - APIGateway Xử Lý Request Response
Mục Tiêu:
- Nhận request data từ client thông qua api-gateway
- Response data về cho client thông qua api-gateway
- Cấu hình đơn giản như: timeout, memory, environment,...
Chuẩn Bị:
- Để tiếp tục đọc và hiểu những phần sau, cần phải có một lambda function được tạo trước đó.
- Nếu bạn chưa tạo một lambda function thì tham khảo here để hiểu các phần sau.
Giải Quyết Vấn Đề
1. Nhận request data:
- http body có dạng.
{
"request_id":"123123213",
"data": {
"name":"open dev"
}
}
- Trong lambda muốn get data từ client, thì sử dụng api-gateway events APIGatewayProxyRequest. Struct này là của aws-sdk trong struct chỉ cần quan tâm field Body và dùng json.Unmarshal
// APIGatewayProxyRequest contains data coming from the API Gateway proxy
type APIGatewayProxyRequest struct {
Resource string `json:"resource"` // The resource path defined in API Gateway
Path string `json:"path"` // The url path for the caller
HTTPMethod string `json:"httpMethod"`
Headers map[string]string `json:"headers"`
MultiValueHeaders map[string][]string `json:"multiValueHeaders"`
QueryStringParameters map[string]string `json:"queryStringParameters"`
MultiValueQueryStringParameters map[string][]string `json:"multiValueQueryStringParameters"`
PathParameters map[string]string `json:"pathParameters"`
StageVariables map[string]string `json:"stageVariables"`
RequestContext APIGatewayProxyRequestContext `json:"requestContext"`
Body string `json:"body"`
IsBase64Encoded bool `json:"isBase64Encoded,omitempty"`
}
- Khai báo một struct để unmarshal data
type RequestBodyAPIGW struct {
RequestID string `json:"request_id"`
Data interface{} `json:"data"`
}
- Unmarshal data:
json.Unmarshal([]byte(eventReq.Body), &req)
2. Response data:
- client muốn response có format. Trong đó field data thì trả về dữ liệu giống với request truyền vào.
{
"data": {
"name": "open dev"
},
"responseId": "123123213",
"responseMessage": "successfully",
"responseTime": "2022-12-17T03:53:42.325+00:00"
}
- Để lambda return data, thì cũng sử dụng api-gateway events APIGatewayProxyResponse.
// APIGatewayProxyResponse configures the response to be returned by API Gateway for the request
type APIGatewayProxyResponse struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers"`
MultiValueHeaders map[string][]string `json:"multiValueHeaders"`
Body string `json:"body"`
IsBase64Encoded bool `json:"isBase64Encoded,omitempty"`
}
- Thông thường response sẽ có 2 dạng, api xảy ra error hoặc không. Mình viết 2 hàm response và gắn vào field Body.
type HttpResponse struct {
Uuid string // uuid, indicator per api
Err error
Time string // time tracing
Data interface{}
}
func responseOk(respBody HttpResponse) string {
var buf bytes.Buffer
mapRes := map[string]interface{}{
"responseId": respBody.Uuid,
"responseMessage": "successfully",
"responseTime": respBody.Time,
}
if respBody.Data != nil {
mapRes["data"] = respBody.Data
}
body, errMarshal := json.Marshal(mapRes)
if errMarshal != nil {
log.Default().Println("marshal response err", errMarshal)
}
json.HTMLEscape(&buf, body)
return buf.String()
}
func responseErr(respBody HttpResponse) string {
var buf bytes.Buffer
mapRes := map[string]interface{}{
"responseId": respBody.Uuid,
"responseMessage": respBody.Err.Error(),
"responseTime": respBody.Time,
}
body, errMarshal := json.Marshal(mapRes)
if errMarshal != nil {
log.Default().Println("marshal response err", errMarshal)
}
json.HTMLEscape(&buf, body)
return buf.String()
}
3. Cấu hình timeout, memory, env:
- Để cấu hình 3 thuộc tính trên, đơn giản chỉ cần mở file serverless.yml.
- Trong block provider: ta time 2 field timeout và memorySize, thì đã set được timeout và resource.
provider:
timeout: 6
memorySize: 256
- Environment của lambda, cũng tương tự trong block provider:, thêm environment: trong lambda sẽ có env env_test có giá trị là value-test
provider:
environment:
env_test: "value-test"
Deploy Function:
- file Makefile:
build:
env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go
clean:
rm -rf ./bin
deploy: clean build
sls deploy --verbose
- run:
make deploy
- output:
Test:
Full source:
- file golang:
package main
import (
"bytes"
"context"
"encoding/json"
"log"
"time"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type Response events.APIGatewayProxyResponse
type RequestBodyAPIGW struct {
RequestID string `json:"requestId"`
Data interface{} `json:"data"`
}
// type ResponseBodyAPIGW struct {
// RequestID string `json:"request_id"`
// Message string `json:"message"`
// }
func Handler(ctx context.Context, eventReq events.APIGatewayProxyRequest) (Response, error) {
var (
req = RequestBodyAPIGW{}
resp = Response{
StatusCode: 404,
IsBase64Encoded: false,
Headers: map[string]string{
"Content-Type": "application/json",
"X-MyCompany-Func-Reply": "hello-handler",
},
}
)
err := json.Unmarshal([]byte(eventReq.Body), &req)
if err != nil {
resp.Body = ParseResponse(HttpResponse{
Uuid: req.RequestID,
Err: err})
return resp, nil
}
resp.StatusCode = 200
resp.Body = ParseResponse(HttpResponse{Uuid: req.RequestID, Data: req.Data})
return resp, nil
}
func main() {
lambda.Start(Handler)
}
type HttpResponse struct {
Uuid string // uuid, indicator per api
Err error
Time string // time tracing
Data interface{}
}
func ParseResponse(respBody HttpResponse) string {
respBody.Time = time.Now().Format("2006-01-02T15:04:05.000-07:00")
if respBody.Err != nil {
return responseErr(respBody)
}
return responseOk(respBody)
}
func responseOk(respBody HttpResponse) string {
var buf bytes.Buffer
mapRes := map[string]interface{}{
"responseId": respBody.Uuid,
"responseMessage": "successfully",
"responseTime": respBody.Time,
}
if respBody.Data != nil {
mapRes["data"] = respBody.Data
}
body, errMarshal := json.Marshal(mapRes)
if errMarshal != nil {
log.Default().Println("marshal response err", errMarshal)
}
json.HTMLEscape(&buf, body)
return buf.String()
}
func responseErr(respBody HttpResponse) string {
var buf bytes.Buffer
mapRes := map[string]interface{}{
"responseId": respBody.Uuid,
"responseMessage": respBody.Err.Error(),
"responseTime": respBody.Time,
}
body, errMarshal := json.Marshal(mapRes)
if errMarshal != nil {
log.Default().Println("marshal response err", errMarshal)
}
json.HTMLEscape(&buf, body)
return buf.String()
}
- file serverless.yml
service: lambda-go
frameworkVersion: '3'
provider:
name: aws
runtime: go1.x
timeout: 6
memorySize: 256
environment:
env_test: "value-test"
package:
patterns:
- '!./**'
- ./bin/**
functions:
hello:
handler: bin/hello
events:
- httpApi:
path: /hello
method: post
Liên Hệ
- facebook: https://www.facebook.com/phucducdev/
- group: https://www.facebook.com/groups/575250507328049
- gmail: ducnp09081998@gmail.com
- linkedin: https://www.linkedin.com/in/phucducktpm/
- hashnode: https://hashnode.com/@OpenDev
- telegram: https://t.me/OpenDevGolang
All rights reserved