全部產品
Search
文件中心

Function Compute:配置API Gateway觸發器

更新時間:Jul 06, 2024

Function Compute支援API Gateway作為事件來源,即支援將Function Compute設定為API的後端服務。當請求設定後端服務為Function Compute時,API Gateway會觸發關聯的函數執行一次,Function Compute將執行結果返回給API Gateway。本文介紹在Function Compute控制台配置API Gateway觸發函數執行的流程,包括配置函數的入口參數、編寫函數代碼並測試等。

前提條件

步驟一:配置函數的入口參數

API Gateway觸發函數執行時,API Gateway的資訊以event的形式作為輸入參數傳給函數,您可以將API Gateway傳入的event資訊作為參數,調試函數代碼編寫是否正確。

  1. 在函數詳情頁面,單擊函數代碼頁簽,然後單擊測試函數右側xialatubiao表徵圖,從下拉式清單中,選擇配置測試參數
  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"
    }  
    說明
    • 如果isBase64Encoded的值為true,表示API Gateway傳給Function Compute的body內容已進行Base64編碼。Function Compute需要先對body內容進行Base64解碼後再處理。
    • 如果isBase64Encoded的值為false,表示API Gateway沒有對body內容進行Base64編碼,在函數中可以直接擷取body內容。

步驟二:編寫函數代碼並測試

  1. 登入Function Compute控制台

  2. 在左側導覽列,單擊服務及函數
  3. 在頂部功能表列,選擇地區。

  4. 服務列表頁面,找到目標服務,在其右側操作列單擊函數管理

  5. 函數管理頁面,單擊目標函數名稱。

  6. 在函數詳情頁面,單擊函數代碼頁簽,在代碼編輯器中編寫代碼,然後單擊部署代碼
    不同語言的範例程式碼如下:
    • Node.js
      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)
      };                               
    • Python
      # -*- 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
      <?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

      使用Java編程時,必須要實現一個類,需要實現Function Compute預定義的handler,目前有兩個預定義的handler可以實現(任選其一即可)。Function ComputeJava運行環境,請參見編譯部署程式碼封裝

      • (推薦)使用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;
                }
            }
        
            // Function Compute需要以以下JSON格式返回對API Gateway的響應。
            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;
                }
            }
        }                                        

  7. 單擊函數代碼頁簽的測試函數

步驟三:驗證結果

執行完成後,您可以在函數代碼頁簽的上方查看執行結果。

Function Compute需要將執行的結果按照以下JSON格式返回給API Gateway,然後由API Gateway解析。返回格式樣本如下:
{
    "isBase64Encoded":true|false,
    "statusCode":httpStatusCode,
    "headers":{response headers},
    "body":"..."
}       
說明
  • 如果Function Compute返回給API Gateway的結果不符合格式要求,API Gateway會返回503 Service Unavailable。
  • body內容為二進位時,需對其進行Base64編碼,即isBase64Encoded設定為true;當body內容為非二進位時,無需對其進行Base64編碼,即isBase64Encoded設定為false

常見問題

API Gateway觸發函數執行時報503,查看函數日誌,函數已經執行成功了,這是怎麼回事?

API Gateway和Function Compute的對接有格式要求,如果Function Compute返回給API Gateway的結果沒有按規定的格式返回,那麼API Gateway就認為後端服務不可用。關於API Gateway和Function Compute的對接格式要求,請參見觸發器Event格式Function Compute的返回參數格式

如何設定返迴響應的content-type?

如圖所示,在API設定的時候可以設定響應的content-type。詳細內容,請參見建立後端為Function Compute的APIcontent-type

API Gateway觸發Function Compute執行,已經調通的函數,一段時間不調用,再次調用會報503,這是什麼原因?

一段時間不調用後,函數重新調用需要準備執行環境,有冷啟動時延,在API Gateway設定的逾時時間內沒有調用完,API Gateway會認為後端服務不可用。延長API Gateway的逾時時間即可解決問題。

為什麼函數中接收到API Gateway傳過來的body是經過了Base64編碼的?

API Gateway對FORM形式的body傳輸是不進行Base64編碼的(使用FORM形式需要在API Gateway選擇入參映射),其他形式body都會進行Base64編碼,避免內容傳輸錯誤或者丟失。建議您在使用時,先判斷event中isBase64是否為true。如果isBase64為true,則body需要在函數中進行解碼。關於API Gateway傳給Function Compute的event格式,請參見觸發器Event格式

更多資訊

除了Function Compute控制台,您還可通過以下方式配置觸發器:

  • 通過Serverless Devs工具配置觸發器。更多操作,請參見Serverless Devs

  • 通過SDK配置觸發器。更多操作,請參見SDK列表

如需對建立的觸發器進行修改或刪除,請參見觸發器管理