Golang AWS API 网关 查找值开头'e'无效字符



我正在尝试创建一个连接到 lambda 的 API 网关,该网关解析带有把手的 HTML 模板,然后返回它,但是当我在本地甚至在使用 AWS 的测试网址上运行它时,我收到此错误。

{
"errorMessage": "invalid character 'e' looking for beginning of value",
"errorType": "SyntaxError"
}

这是我的SAM模板

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: data-template-renderer
Parameters:
Stage:
Type: String
AllowedValues:
- dev
- staging
- production
Description: environment values
Resources:
# Defining the API Resource here means we can define the stage name rather than
# always being forced to have staging/prod. Also means we can add a custom domain with
# to the API gateway within this template if needed. Unfortunately there is a side effect
# where it creates a stage named "Stage". This is a known bug and the issue can be
# found at https://github.com/awslabs/serverless-application-model/issues/191
DataTemplateRendererApi:
Type: AWS::Serverless::Api
Properties:
Name: !Sub "${Stage}-data-template-renderer"
StageName: !Ref Stage
DefinitionBody:
swagger: "2.0"
basePath: !Sub "/${Stage}"
info:
title: !Sub "${Stage}-data-template-renderer"
version: "1.0"
consumes:
- application/json
produces:
- application/json
- text/plain
- application/pdf
paths:
/render:
post:
x-amazon-apigateway-integration:
uri:
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RenderTemplate.Arn}/invocations"
httpMethod: POST
type: aws_proxy
x-amazon-apigateway-binary-media-types:
- "*/*"
RenderTemplate:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
ENV: !Ref Stage
Runtime: go1.x
CodeUri: build
Handler: render_template
FunctionName: !Sub 'render_template-${Stage}'
Timeout: 30
Tracing: Active
Events:
RenderTemplateEndpoint:
Type: Api
Properties:
RestApiId: !Ref DataTemplateRendererApi
Path: /render
Method: POST
Policies:
- !Ref S3AccessPolicy
- CloudWatchPutMetricPolicy: {}
S3AccessPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: data-template-renderer-s3-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: s3:GetObject
Resource: !Sub "arn:aws:s3:::*"
- Effect: Allow
Action: s3:PutObject
Resource: !Sub "arn:aws:s3:::*"

下面是我用于 Lambda 的代码。请原谅它可能不是你见过的最好的 Golang 代码,但当涉及到 Golang 时,我是一个初学者,因为我主要是一个 PHP 开发人员,但我工作的公司正在创建很多 Golang lambda,所以我开始学习它。

package main
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/aymerick/raymond"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/SebastiaanKlippert/go-wkhtmltopdf"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
var sess *session.Session
func init() {
// Setup AWS S3 Session (build once use every function)
sess = session.Must(session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),
}))
}
func main() {
lambda.Start(handleRequest)
}
type TemplateRendererRequest struct {
Template string `json:"template"`
Bucket string `json:"bucket"`
Type string `json:"type"`
Data map[string]interface{} `json:"data"`
}
type EmailResponse struct {
Email string `json:"email"`
}
func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// Unmarshal json request body into a TemplateRendererRequest struct that mimics the json payload
requestData := TemplateRendererRequest{}
err := json.Unmarshal([]byte(request.Body), &requestData)
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}
// Get the template object from S3
result, err := s3.New(sess).GetObject(&s3.GetObjectInput{
Bucket: aws.String(requestData.Bucket),
Key:    aws.String(requestData.Template),
})
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}
defer result.Body.Close()
// The S3 Object Body is of type io.ReadCloser
// below we create a bytes buffer and then convert it to a string so we can use it later
buf := new(bytes.Buffer)
buf.ReadFrom(result.Body)
templateString := buf.String()
// Parse template through Handlebars library
parsedTemplate, err := parseTemplate(templateString, requestData)
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}
switch requestData.Type {
case "pdf":
return handlePDFRequest(parsedTemplate, requestData)
default:
return handleEmailRequest(parsedTemplate)
}
}
func handleEmailRequest(parsedTemplate string) (events.APIGatewayProxyResponse, error) {
// Put email parsed template in a struct to return in the form of JSON
emailResponse := EmailResponse{
Email: parsedTemplate,
}
// JSON encode the emailResponse
response, err := JSONMarshal(emailResponse)
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}
return events.APIGatewayProxyResponse{
Body: string(response),
StatusCode: 200,
Headers: map[string]string{
"Content-Type": "application/json",
},
}, nil
}
func parseTemplate(templateString string, request TemplateRendererRequest) (string, error) {
result, err := raymond.Render(templateString, request.Data)
return result, err
}
func handlePDFRequest(parsedTemplate string, requestData TemplateRendererRequest) (events.APIGatewayProxyResponse, error) {
pdfBytes, err := GeneratePDF(parsedTemplate)
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}
keyNameParts := strings.Split(requestData.Template, "/")
keyName := keyNameParts[len(keyNameParts)-1]
pdfName := fmt.Sprintf("%s_%s.pdf", keyName, time.Now().UTC().Format("20060102150405"))
_, err = s3.New(sess).PutObject(&s3.PutObjectInput{
Bucket: aws.String(requestData.Bucket),
Key:    aws.String(pdfName),
Body:   bytes.NewReader(pdfBytes),
})
b64Pdf := base64.StdEncoding.EncodeToString(pdfBytes)
return events.APIGatewayProxyResponse{
Body: b64Pdf,
StatusCode: 200,
Headers: map[string]string{
"Content-Type": "application/pdf",
},
IsBase64Encoded: true,
}, nil
}
func GeneratePDF(templateString string) ([]byte, error) {
pdfg, err := wkhtmltopdf.NewPDFGenerator()
if err != nil {
return nil, err
}
// Pass S3 Object body (as reader io.Reader) directly into wkhtmltopdf
pdfg.AddPage(wkhtmltopdf.NewPageReader(strings.NewReader(templateString)))
// Create PDF document in internal buffer
if err := pdfg.Create(); err != nil {
return nil, err
}
// Return PDF as bytes array
return pdfg.Bytes(), nil
}
// https://stackoverflow.com/questions/28595664/how-to-stop-json-marshal-from-escaping-and
func JSONMarshal(t interface{}) ([]byte, error) {
buffer := &bytes.Buffer{}
encoder := json.NewEncoder(buffer)
encoder.SetEscapeHTML(false)
err := encoder.Encode(t)
return buffer.Bytes(), err
}

我试图高低搜索解决方案,但找不到为什么我得到这个非常奇怪的错误,这不是很有帮助。 感谢您抽出宝贵时间和帮助

感谢@Anzel为我指出正确方向的人,我决定查看request.Body,似乎通过将*/*添加到 API 网关二进制媒体类型,现在甚至请求都经过编码,瞧,第一个字母确实是消息所说的e

所以我改变了这个:

// Unmarshal json request body into a TemplateRendererRequest struct that mimics the json payload
requestData := TemplateRendererRequest{}
err := json.Unmarshal([]byte(request.Body), &requestData)
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}

对此:

// Unmarshal json request body into a TemplateRendererRequest struct that mimics the json payload
requestData := TemplateRendererRequest{}
b64String, _ := base64.StdEncoding.DecodeString(request.Body)
rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()
if err != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", err.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, err
}
jsonMarshalErr := json.Unmarshal(bodyBytes, &requestData)
if jsonMarshalErr != nil {
return events.APIGatewayProxyResponse{
Body: fmt.Errorf("Error: %s ", jsonMarshalErr.Error()).Error(),
StatusCode: 400,
Headers: map[string]string{
"Content-Type": "text/plain",
},
}, jsonMarshalErr
}

从我在网上看到的,你也可以改变这一点:

rawIn := json.RawMessage(b64String)
bodyBytes, err := rawIn.MarshalJSON()

对此:

[]byte(b64String)

当我现在执行 CURL 请求并将其输出到文件时,我正确获得了 PDF。