API网关触发器

更新时间:2025-03-05 10:12:52

函数计算支持API网关作为事件源,即支持将函数计算设置为API的后端服务。当有请求到达后端服务设置为函数计算API网关时,会触发关联的函数执行一次,函数计算将执行结果返回给API网关。

背景信息

API网关触发器与HTTP触发器类似,可应用于搭建Web应用。相较于HTTP触发器,您可以使用API网关进行IP白名单或黑名单设置等高级操作。

API网关支持事件函数和Web函数两种函数类型作为其后端服务,将API网关与函数计算服务对接后,即可通过API形式安全地对外开放函数,并且解决认证、流量控制、数据转换等问题。

创建事件函数并对接API网关

步骤一:创建事件触发函数

在函数计算3.0控制台创建事件函数,具体操作步骤请参见创建事件函数

步骤二:创建后端服务为函数计算的API

API网关中定义后端服务,并配置后端服务地址来对接函数计算服务。

  1. 登录API网关控制台,选择地域,在左侧导航栏选择API管理 > 后端服务,单击右上角创建后端服务,配置如下信息,单击确定

    image

  2. 在后端服务页面,单击刚刚创建的后端服务,进入后端服务定义页,选择线上页签,在基本信息处单击创建,选择步骤一创建的事件函数,然后单击发布

    image

  3. 创建分组。

    说明

    建议创建与函数相同地域的API分组,如果不是相同地域,API需要通过公网访问您的函数计算服务,这将产生流量费用。若您对数据安全和网络延迟有较高要求,请选择API与函数计算为同一地域。

  4. 创建并发布API。

    重点配置项设置如下,其余保持默认即可。

    image

    配置项

    取值示例

    配置项

    取值示例

    安全认证

    无认证

    后端配置

    使用已有的后端服务

    后端服务类型

    函数计算

    产品版本

    函数计算3.0

    函数类型

    事件函数

    后端服务

    选择刚才创建的事件函数后端服务。

步骤三:编写函数代码

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

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

  3. 在函数详情页面的代码页签,在代码编辑器中编写代码,然后单击部署代码

    不同语言的示例代码如下:

    Node.js
    Python
    PHP
    Java
    module.exports.handler = function(event, context, callback) { 
       var event = JSON.parse(event);
       var content = {
         path: event.path,
         method: event.method,
         headers: event.headers,
         queryParameters: event.queryParameters,
         pathParameters: event.pathParameters,
         body: event.body
       // 您可以在这里编写您自己的逻辑。  
       }
       var response = {
            isBase64Encoded: false,
            statusCode: '200',
            headers: {
              'x-custom-header': 'header value'
            },
            body: content
          }; 
       callback(null, response)
    };                               
    # -*- coding: utf-8 -*-
    import json
    
    def handler(event, context):
        event = json.loads(event)
        content = {
            'path': event['path'],
            'method': event['httpMethod'],
            'headers': event['headers'],
            'queryParameters': event['queryParameters'],
            'pathParameters': event['pathParameters'],
            'body': event['body']
        }
        # 您可以在这里编写您自己的逻辑。 
        rep = {
            "isBase64Encoded": "false",
            "statusCode": "200",
            "headers": {
                "x-custom-header": "no"
            },
            "body": content
        }
        return json.dumps(rep)   
    <?php
    function handler($event, $context) {
        $event   = json_decode($event, $assoc = true);
        $content = [
            'path'            => $event['path'],
            'method'          => $event['httpMethod'],
            'headers'         => $event['headers'],
            'queryParameters' => $event['queryParameters'],
            'pathParameters'  => $event['pathParameters'],
            'body'            => $event['body'],
        ];
    
        $rep = [
            "isBase64Encoded" => "false",
            "statusCode"      => "200",
            "headers"         => [
                "x-custom-header" => "no",
            ],
            "body"            => $content,
        ];
    
        return json_encode($rep);
    } 

    使用Java编程时,必须要实现一个类,需要实现函数计算预定义的handler,目前有两个预定义的handler可以实现(任选其一即可)。函数计算Java运行环境,请参见编译部署代码包

    • (推荐)使用PojoRequestHandler<I, O> handler。

      import com.aliyun.fc.runtime.Context;
      import com.aliyun.fc.runtime.PojoRequestHandler;
      import java.util.HashMap;
      import java.util.Map;
      
      public class ApiTriggerDemo implements PojoRequestHandler<ApiRequest, ApiResponse> {
      
          public ApiResponse handleRequest(ApiRequest request, Context context) {
              // 获取API请求信息。
              context.getLogger().info(request.toString());
              String path = request.getPath();
              String httpMethod = request.getHttpMethod();
              String body = request.getBody();
              context.getLogger().info("path: " + path);
              context.getLogger().info("httpMethod: " + httpMethod);
              context.getLogger().info("body: " + body);
      
              // 您可以在这里编写您自己的逻辑。
      
              // API返回示例。
              Map headers = new HashMap();
              boolean isBase64Encoded = false;
              int statusCode = 200;
              String returnBody = "";
              return new ApiResponse(headers,isBase64Encoded,statusCode,returnBody);
          }
      }                                        
      • 两个POJO类、ApiRequest类和ApiResponse类定义如下。

        说明

        POJO类的set()get()方法要写全。

        import java.util.Map;
        
        public class ApiRequest {
            private String path;
            private String httpMethod;
            private Map headers;
            private Map queryParameters;
            private Map pathParameters;
            private String body;
            private boolean isBase64Encoded;
        
            @Override
            public String toString() {
                return "Request{" +
                        "path='" + path + '\'' +
                        ", httpMethod='" + httpMethod + '\'' +
                        ", headers=" + headers +
                        ", queryParameters=" + queryParameters +
                        ", pathParameters=" + pathParameters +
                        ", body='" + body + '\'' +
                        ", isBase64Encoded=" + isBase64Encoded +
                        '}';
            }
        
            public String getPath() {
                return path;
            }
        
            public void setPath(String path) {
                this.path = path;
            }
        
            public String getHttpMethod() {
                return httpMethod;
            }
        
            public void setHttpMethod(String httpMethod) {
                this.httpMethod = httpMethod;
            }
        
            public Map getHeaders() {
                return headers;
            }
        
            public void setHeaders(Map headers) {
                this.headers = headers;
            }
        
            public Map getQueryParameters() {
                return queryParameters;
            }
        
            public void setQueryParameters(Map queryParameters) {
                this.queryParameters = queryParameters;
            }
        
            public Map getPathParameters() {
                return pathParameters;
            }
        
            public void setPathParameters(Map pathParameters) {
                this.pathParameters = pathParameters;
            }
        
            public String getBody() {
                return body;
            }
        
            public void setBody(String body) {
                this.body = body;
            }
        
            public boolean getIsBase64Encoded() {
                return this.isBase64Encoded;
            }
        
            public void setIsBase64Encoded(boolean base64Encoded) {
                this.isBase64Encoded = base64Encoded;
            }
        }                                      
        import java.util.Map;
        
        public class ApiResponse {
            private Map headers;
            private boolean isBase64Encoded;
            private int statusCode;
            private String body;
        
            public ApiResponse(Map headers, boolean isBase64Encoded, int statusCode, String body) {
                this.headers = headers;
                this.isBase64Encoded = isBase64Encoded;
                this.statusCode = statusCode;
                this.body = body;
            }
        
            public Map getHeaders() {
                return headers;
            }
        
            public void setHeaders(Map headers) {
                this.headers = headers;
            }
        
            public boolean getIsBase64Encoded() {
                return isBase64Encoded;
            }
        
            public void setIsBase64Encoded(boolean base64Encoded) {
                this.isBase64Encoded = base64Encoded;
            }
        
            public int getStatusCode() {
                return statusCode;
            }
        
            public void setStatusCode(int statusCode) {
                this.statusCode = statusCode;
            }
        
            public String getBody() {
                return body;
            }
        
            public void setBody(String body) {
                this.body = body;
            }
        }                                       
      • pom.xml文件如下。

        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>
        
            <groupId>apiTrigger</groupId>
            <artifactId>apiTrigger</artifactId>
            <version>1.0-SNAPSHOT</version>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <source>1.8</source>
                            <target>1.8</target>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
            <dependencies>
                <dependency>
                    <groupId>com.aliyun.fc.runtime</groupId>
                    <artifactId>fc-java-core</artifactId>
                    <version>1.0.0</version>
                </dependency>
            </dependencies>   
        </project>                                        
    • 使用StreamRequestHandler handler。

      使用该handler,需要将输入的InputStream转换为对应的POJO类,示例代码如下。

      pom.xml文件配置与使用PojoRequestHandler<I, O> handler相同。

      import com.aliyun.fc.runtime.Context;
      import com.aliyun.fc.runtime.StreamRequestHandler;
      import com.aliyun.fc.runtime.Context;
      import com.google.gson.Gson;
      import java.io.*;
      import java.util.Base64;
      import java.util.HashMap;
      import java.util.Map;
      
      public class ApiTriggerDemo2 implements StreamRequestHandler {
      
          public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
              try {
                  // 将InputStream转化成字符串。
                  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                  StringBuffer stringBuffer = new StringBuffer();
                  String string = "";
                  while ((string = bufferedReader.readLine()) != null) {
                      stringBuffer.append(string);
                  }
                  String input = stringBuffer.toString();
                  context.getLogger().info("inputStream: " + input);
                  Request req = new Gson().fromJson(input, Request.class);
                  context.getLogger().info("input req: ");
                  context.getLogger().info(req.toString());
                  String bodyReq = req.getBody();
                  Base64.Decoder decoder = Base64.getDecoder();
                  context.getLogger().info("body: " + new String(decoder.decode(bodyReq)));
      
                  // 您可以在这里处理您自己的逻辑。
      
                  // 返回结构。
                  Map headers = new HashMap();
                  headers.put("x-custom-header", " ");
                  boolean isBase64Encoded = false;
                  int statusCode = 200;
                  Map body = new HashMap();
                  Response resp = new Response(headers, isBase64Encoded, statusCode, body);
                  String respJson = new Gson().toJson(resp);
                  context.getLogger().info("outputStream: " + respJson);
                  outputStream.write(respJson.getBytes());
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      outputStream.close();
                      inputStream.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
          class Request {
              private String path;
              private String httpMethod;
              private Map headers;
              private Map queryParameters;
              private Map pathParameters;
              private String body;
              private boolean isBase64Encoded;
      
              @Override
              public String toString() {
                  return "Request{" +
                          "path='" + path + '\'' +
                          ", httpMethod='" + httpMethod + '\'' +
                          ", headers=" + headers +
                          ", queryParameters=" + queryParameters +
                          ", pathParameters=" + pathParameters +
                          ", body='" + body + '\'' +
                          ", isBase64Encoded=" + isBase64Encoded +
                          '}';
              }
      
              public String getBody() {
                  return body;
              }
          }
      
          // 函数计算需要以以下JSON格式返回对API网关的响应。
          class Response {
              private Map headers;
              private boolean isBase64Encoded;
              private int statusCode;
              private Map body;
      
              public Response(Map headers, boolean isBase64Encoded, int statusCode, Map body) {
                  this.headers = headers;
                  this.isBase64Encoded = isBase64Encoded;
                  this.statusCode = statusCode;
                  this.body = body;
              }
          }
      }                                        

步骤四:配置函数的入口参数

API网关触发函数执行时,API网关的信息以event的形式作为输入参数传给函数,您可以将API网关传入的event信息作为参数,调试函数代码编写是否正确。

  1. 在函数详情页面的代码页签,单击测试函数右侧的image.png图标,从下拉列表中,选择配置测试参数

  2. 配置测试参数面板,选择创建新测试事件编辑已有测试事件,填写事件名称和事件内容,然后单击确定。

    event格式示例如下所示:

    {
        "path":"api request path",
        "httpMethod":"request method name",
        "headers":{all headers,including system headers},
        "queryParameters":{query parameters},
        "pathParameters":{path parameters},
        "body":"string of request payload",
        "isBase64Encoded":"true|false, indicate if the body is Base64-encode"
    }  

    event参数中不同属性字段的解释如下表所示。

    参数

    类型

    描述

    参数

    类型

    描述

    path

    String

    API请求路径。

    httpMethod

    String

    请求的方法名称,例如GET、POST、PUTDELETE等。

    headers

    Object

    包含所有请求头信息,包括系统头和自定义头。

    queryParameters

    Object

    查询参数,通常在 URL 的问号后面以键值对的形式出现。

    pathParameters

    Object

    路径参数,通常是 URL 中的一部分,用于标识特定资源。

    body

    String

    请求体。

    isBase64Encoded

    Boolean

    是否对请求体body进行了Base64编码。

    说明
    • 如果isBase64Encoded的值为true,表示API网关传给函数计算的body内容已进行Base64编码。函数计算需要先对body内容进行Base64解码后再处理。

    • 如果isBase64Encoded的值为false,表示API网关没有对body内容进行Base64编码,在函数中可以直接获取body内容。

  3. 单击测试函数

步骤五:验证结果

执行完成后,您可以在函数代码页签的上方查看执行结果。

函数计算需要将执行的结果按照以下JSON格式返回给API网关,然后由API网关解析。返回格式示例如下:

{
    "isBase64Encoded":true|false,
    "statusCode":httpStatusCode,
    "headers":{response headers},
    "body":"..."
}       

事件函数对接API网关的格式要求

API网关调用函数计算服务时,会将API的相关数据转换为Map形式传给函数计算服务。函数计算服务处理后,按照Output Format格式返回statusCode、headers、body等相关数据。API网关再将函数计算返回的内容映射到statusCode、headers、body等位置返回给客户端。

principle

创建Web函数并对接API网关

步骤一:创建Web函数

在函数计算3.0控制台中创建一个Web函数,具体操作步骤,请参见创建Web函数

默认为创建的Web函数创建一个HTTP触发器,复制内网访问地址供后续使用。

image

步骤二:创建后端服务

API网关中定义后端服务,并配置后端服务地址来对接函数计算服务。

  1. 登录API网关控制台,选择地域,在左侧导航栏选择API管理 > 后端服务,单击右上角创建后端服务,配置如下信息,单击确定

    image

  2. 在后端服务页面,单击刚刚创建的后端服务,进入后端服务定义页,选择线上页签,在基本信息处单击创建,填写步骤一创建的Web函数的触发器内网访问地址,然后单击发布

    image

步骤三:创建并发布API

创建API使得外部应用能够按照指定的方式调用内部的Web函数服务,使用API分组组织和管理多个相关的API接口,便于实施统一的安全策略和流量控制措施。

  1. 登录API网关控制台,在左侧导航栏选择API管理 > 分组管理,单击创建分组以便对API进行管理。

  2. 创建分组弹框页面,选择实例,输入分组名称FC-GroupBasePath/,单击确定

    image

    说明

    建议创建与函数相同地域的API分组,如果不是相同地域,API需要通过公网访问您的函数计算服务,这将产生流量费用。若您对数据安全和网络延迟有较高要求,请选择API与函数计算为同一地域。

  3. 在分组列表页面,单击目标分组右侧操作列的管理API,然后单击创建API,配置如下信息,单击下一步

    image

  4. 定义API请求配置页签,配置请求Path/,其他信息保持默认,单击下一步

  5. 定义API后端服务配置页签,如图所示进行配置,单击下一步

    image

  6. 定义返回结果配置页签,保持系统默认配置,单击创建,在创建成功之后,在弹出的提示对话框单击发布

  7. 发布API对话框,设置以下配置项,然后单击发布

    image

步骤四:创建应用和API授权

应用(APP)是调用API服务时的身份,在步骤三:创建并发布API时,认证方式选择的是阿里云APP认证,因此在API发布后,还需要创建APP,并建立APPAPI的授权关系,才能够正常访问。

  1. 登录API网关控制台,在左侧导航栏选择API调用 > 应用管理

  2. 应用与授权页面,单击右上角创建APP。在创建应用页面,输入应用名称fcApp,单击确定

  3. 单击已创建好的fcApp应用名称,进入应用详情页面,可以看到阿里云APP下有两种认证方式,AppKeyAppCodeAppKey方式有一组AppKeyAppSecret,您可以理解为账号密码,调用API的时候需要将AppKey作为参数传入,AppSecret用于签名计算,网关会校验这对密钥对您进行身份认证。

    image

  4. 在左侧导航栏选择API管理 > API列表,在API列表页面,找到已创建好的API,在其操作列选择image> 授权

  5. 在授权页面,配置选择要授权的环境线上。搜索之前创建的应用fcApp,单击添加确定,提示授权成功,即成功授权。

    image

步骤五:验证结果

下面介绍使用APPCode的认证方式在您的业务系统中调用已发布的API,本文以使用Curl命令调用为例。

登录API网关控制台,在左侧导航栏选择API调用>应用管理,在应用与授权页面找到授权的APP,单击进入获取APPCode。然后按照以下示例调用API。

curl -i -X GET "http://fd6f8e2b7bf44ab181a56****-cn-hangzhou.alicloudapi.com" -H "Authorization:APPCODE 7d2b7e4945ce44028ab00***"

常见问题

API网关触发函数执行时报502,查看函数日志,函数已经执行成功了,这是怎么回事?

API网关和函数计算的对接有格式要求,如果函数计算返回给API网关的结果没有按规定的格式返回,那么API网关就认为后端服务不可用。关于API网关和函数计算的对接格式要求,请参见触发器Event格式验证结果中函数计算的返回参数格式

如何设置返回响应的content-type?

如图所示,在API设置的时候可以设置返回响应的content-type。详细内容,请参见通过API网关对接函数计算FC3.0(Web函数)content-type

API网关触发函数计算执行,已经调通的函数,一段时间不调用,再次调用会报503,这是什么原因?

一段时间不调用后,函数重新调用需要准备执行环境,有冷启动时延,如果在API网关设置的超时时间内没有调用完,API网关会认为后端服务不可用。延长API网关的超时时间即可解决问题。

为什么函数中接收到API网关传过来的body是经过了Base64编码的?

API网关对FORM形式的body传输是不进行Base64编码的(使用FORM形式需要在API网关选择入参映射),其他形式body都会进行Base64编码,避免内容传输错误或者丢失。建议您在使用时,先判断eventisBase64是否为true。如果isBase64true,则body需要在函数中进行解码。关于API网关传给函数计算的event格式,请参见触发器Event格式

  • 本页导读 (1)
  • 背景信息
  • 创建事件函数并对接API网关
  • 步骤一:创建事件触发函数
  • 步骤二:创建后端服务为函数计算的API
  • 步骤三:编写函数代码
  • 步骤四:配置函数的入口参数
  • 步骤五:验证结果
  • 事件函数对接API网关的格式要求
  • 创建Web函数并对接API网关
  • 步骤一:创建Web函数
  • 步骤二:创建后端服务
  • 步骤三:创建并发布API
  • 步骤四:创建应用和API授权
  • 步骤五:验证结果
  • 常见问题