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