[Golang] AWS Lambda thông qua Serverless Framework – Phần 2

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ì

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 timeoutmemorySize, 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

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ