请求处理程序(Handler)

您可以使用Go请求处理程序响应接收到的事件并执行相应的业务逻辑。本文介绍Go请求处理程序的相关概念、结构特点和使用示例。

说明

如您需要通过HTTP触发器或自定义域名访问函数,请先获取请求结构体再自定义HTTP响应。更多信息,请参见HTTP触发器调用函数

什么是请求处理程序

FC函数的请求处理程序,是函数代码中处理请求的方法。当您的FC函数被调用时,函数计算会运行您提供的Handler方法处理请求。您可以通过函数计算控制台函数入口配置Handler。

对Go语言的FC函数而言,您的请求处理程序被编译为一个可执行的二进制文件。您只需要将FC函数的请求处理程序配置项设置为该可执行文件的文件名即可。

关于FC函数的具体定义和相关操作,请参见创建事件函数

请求处理程序的具体配置均需符合函数计算平台的配置规范。配置规范因请求处理程序类型而异。

使用示例

在Go语言的代码中,您需要引入官方的SDK库aliyun/serverless/fc-runtime-go-sdk/fc,并实现handler函数和main函数。示例如下。

package main

import (
    "fmt"
    "context"

    "github.com/aliyun/fc-runtime-go-sdk/fc"
)

type StructEvent struct {
    Key string `json:"key"`
}

func HandleRequest(ctx context.Context, event StructEvent) (string, error) {
    return fmt.Sprintf("hello, %s!", event.Key), nil
}

func main() {
    fc.Start(HandleRequest)
}

传入的event参数是一个包含key属性的JSON字符串,示例如下。

{
  "key": "value"
}

具体的示例解析如下:

  • package main:在Go语言中,Go应用程序都包含一个名为main的包。

  • import:需要引用函数计算依赖的包,主要包括以下包:

    • github.com/aliyun/fc-runtime-go-sdk/fc函数计算Go语言的核心库。

    • context函数计算Go语言的Context对象。

  • func HandleRequest(ctx context.Context, event StructEvent) (string, error):处理请求的方法(即Handler),需包含将要执行的代码,参数含义如下:

    • ctx context.Context:为您的FC函数调用提供在调用时的运行上下文信息。更多信息,请参见上下文

    • event StructEvent:调用函数时传入的数据,可以支持多种类型。

    • string, error:返回两个值,字符串和错误信息。更多信息,请参见错误处理

    • return fmt.Sprintf("Hi,%s !", event.Key), nil:简单地返回hello信息,其中包含传入的eventnil表示没有报错。

  • func main():运行FC函数代码的入口点,Go程序必须包含main函数。通过添加代码fc.Start(HandleRequest),您的程序即可运行在阿里云函数计算平台。

Event Handler签名

下面列举出了有效的Event Handler签名,其中InputTypeOutputTypeencoding/json标准库兼容。

函数计算会使用json.Unmarshal方法对传入的InputType进行反序列化,以及使用json.Marshal方法对返回的OutputType进行序列化。关于如何反序列化函数的返回数据,请参考JSON Unmarshal

  • func ()

  • func () error

  • func (InputType) error

  • func () (OutputType, error)

  • func (InputType) (OutputType, error)

  • func (context.Context) error

  • func (context.Context, InputType) error

  • func (context.Context) (OutputType, error)

  • func (context.Context, InputType) (OutputType, error)

Handler的使用需遵循以下规则:

  • Handler必须是一个函数。

  • Handler支持0~2个输入参数。如果有2个参数,则第一个参数必须是context.Context

  • Handler支持0~2个返回值。如果有1个返回值,则必须是error类型;如果有2个返回值,则第2个返回值必须是error

函数的Handler示例代码:

更多Handler示例,请参见examples

Context

Context的详细使用方法,请参见上下文

使用HTTP触发器调用函数

示例代码

package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/aliyun/fc-runtime-go-sdk/events"
	"github.com/aliyun/fc-runtime-go-sdk/fc"
)

type HTTPTriggerEvent events.HTTPTriggerEvent
type HTTPTriggerResponse events.HTTPTriggerResponse

func (h HTTPTriggerEvent) String() string {
	jsonBytes, err := json.MarshalIndent(h, "", "  ")
	if err != nil {
		return ""
	}
	return string(jsonBytes)
}

func NewHTTPTriggerResponse(statusCode int) *HTTPTriggerResponse {
	return &HTTPTriggerResponse{StatusCode: statusCode}
}

func (h *HTTPTriggerResponse) String() string {
	jsonBytes, err := json.MarshalIndent(h, "", "  ")
	if err != nil {
		return ""
	}
	return string(jsonBytes)
}

func (h *HTTPTriggerResponse) WithStatusCode(statusCode int) *HTTPTriggerResponse {
	h.StatusCode = statusCode
	return h
}

func (h *HTTPTriggerResponse) WithHeaders(headers map[string]string) *HTTPTriggerResponse {
	h.Headers = headers
	return h
}

func (h *HTTPTriggerResponse) WithIsBase64Encoded(isBase64Encoded bool) *HTTPTriggerResponse {
	h.IsBase64Encoded = isBase64Encoded
	return h
}

func (h *HTTPTriggerResponse) WithBody(body string) *HTTPTriggerResponse {
	h.Body = body
	return h
}

func HandleRequest(event HTTPTriggerEvent) (*HTTPTriggerResponse, error) {
	fmt.Printf("event: %v\n", event)
	if event.Body == nil {
		return NewHTTPTriggerResponse(http.StatusBadRequest).
			WithBody(fmt.Sprintf("the request did not come from an HTTP Trigger, event: %v", event)), nil
	}

	reqBody := *event.Body
	if event.IsBase64Encoded != nil && *event.IsBase64Encoded {
		decodedByte, err := base64.StdEncoding.DecodeString(*event.Body)
		if err != nil {
			return NewHTTPTriggerResponse(http.StatusBadRequest).
				WithBody(fmt.Sprintf("HTTP Trigger body is not base64 encoded, err: %v", err)), nil
		}
		reqBody = string(decodedByte)
	}
	return NewHTTPTriggerResponse(http.StatusOK).WithBody(reqBody), nil
}

func main() {
	fc.Start(HandleRequest)
}

上述示例从SDK中引入了HTTP触发器的请求结构HTTPTriggerEvent,以及响应结构HTTPTriggerResponse。关于HTTP触发调用的请求负载格式和响应负载格式,请参见HTTP触发器调用函数

前提条件

已使用上述示例创建运行环境为Go的函数,并创建HTTP触发器。具体操作,请参见创建事件函数配置HTTP触发器并使用HTTP触发

操作步骤

  1. 登录函数计算控制台,在左侧导航栏,单击函数

  2. 在顶部菜单栏,选择地域,然后在函数页面,单击目标函数。

  3. 在函数详情页面,单击配置页签,然后再左侧导航栏,单击触发器,在触发器页面获取HTTP触发器的公网访问地址。

  4. 执行以下命令调用函数。

    curl -i "https://http-trigger-demo.cn-shanghai.fcapp.run" -d "Hello FC!"
    重要
    • 如果HTTP触发器的认证方式无需认证,您可以直接使用Postman或Curl工具来调用函数。具体操作,请参见本文操作步骤

    • 如果HTTP触发器的认证方式为签名认证或JWT认证,请使用签名方式或JWT认证方式来调用函数。具体操作,请参见认证鉴权

错误处理

本示例代码支持使用HTTP Trigger触发器或者自定义域名调用,如果使用API调用,但配置的测试参数不符合HTTP Trigger请求格式规范,会出现报错。

例如,在控制台上调用,配置请求参数为"Hello, FC!",点击测试函数按钮,会出现报错如下所示。

{
    "statusCode": 400,
    "body": "the request did not come from an HTTP Trigger, event: {\n  \"version\": null,\n  \"rawPath\": null,\n  \"headers\": null,\n  \"queryParameters\": null,\n  \"body\": null,\n  \"isBase64Encoded\": null,\n  \"requestContext\": null\n}"
}

如果想获取原始的请求事件负载,可以使用下面示例中的Handler。

// GetRawRequestEvent: obtain the raw request event
func GetRawRequestEvent(event []byte) (*HTTPTriggerResponse, error) {
	fmt.Printf("raw event: %s\n", string(event))
	return NewHTTPTriggerResponse(http.StatusOK).WithBody(string(event)), nil
}

func main() {
	fc.Start(GetRawRequestEvent)
}