函数计算支持API网关作为事件源,即支持将函数计算设置为API的后端服务。当请求设置后端服务为函数计算时,API网关会触发关联的函数执行一次,函数计算将执行结果返回给API网关。本文介绍在函数计算控制台配置API网关触发函数执行的流程,包括配置函数的入口参数、编写函数代码并测试等。
步骤一:配置函数的入口参数
API网关触发函数执行时,API网关的信息以event的形式作为输入参数传给函数,您可以将API网关传入的event信息作为参数,调试函数代码编写是否正确。
- 在函数详情页面,单击函数代码页签,然后单击图标,从下拉列表中,选择配置测试参数。
- 在配置测试参数面板,选择创建新测试事件或编辑已有测试事件页签,填写事件名称和事件内容。然后单击确定。
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网关传给函数计算的body内容已进行Base64编码。函数计算需要先对body内容进行Base64解码后再处理。
- 如果
isBase64Encoded
的值为false
,表示API网关没有对body内容进行Base64编码,在函数中可以直接获取body内容。
步骤二:编写函数代码并测试
- 登录函数计算控制台。
- 在左侧导航栏,单击服务及函数。
- 在顶部菜单栏,选择地域。
- 在服务列表页面,找到目标服务,在其右侧操作列单击函数管理。
- 在函数管理页面,单击目标函数名称。
- 在函数详情页面,单击函数代码页签,在代码编辑器中编写代码,然后单击保存并部署。
不同语言的示例代码如下:
- 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编程时,必须要实现一个类,需要实现函数计算预定义的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;
}
}
}
- 单击函数代码页签的测试函数。
步骤三:验证结果
执行完成后,您可以在函数代码页签的上方查看执行结果。
函数计算需要将执行的结果按照以下JSON格式返回给API网关,然后由API网关解析。返回格式示例如下:
{
"isBase64Encoded":true|false,
"statusCode":httpStatusCode,
"headers":{response headers},
"body":"..."
}
说明
- 如果函数计算返回给API网关的结果不符合格式要求,API网关会返回503 Service Unavailable。
- 当body内容为二进制时,需对其进行Base64编码,即isBase64Encoded设置为true;当body内容为非二进制时,无需对其进行Base64编码,即isBase64Encoded设置为false。
常见问题
API网关触发函数执行时报503,查看函数日志,函数已经执行成功了,这是怎么回事?
API网关和函数计算的对接有格式要求,如果函数计算返回给API网关的结果没有按规定的格式返回,那么API网关就认为后端服务不可用。关于API网关和函数计算的对接格式要求,请参见触发器event格式和函数计算的返回参数格式。
如何设置返回响应的content-type?
API网关触发函数计算执行,已经调通的函数,一段时间不调用,再次调用会报503,这是什么原因?
一段时间不调用后,函数重新调用需要准备执行环境,有冷启动时延,在API网关设置的超时时间内没有调用完,API网关会认为后端服务不可用。延长API网关的超时时间即可解决问题。
为什么函数中接收到API网关传过来的body是经过了Base64编码的?
API网关对FORM形式的body传输是不进行Base64编码的(使用FORM形式需要在API网关选择入参映射),其他形式body都会进行Base64编码,避免内容传输错误或者丢失。建议您在使用时,先判断event中isBase64是否为true。如果isBase64为true,则body需要在函数中进行解码。关于API网关传给函数计算的event格式,请参见触发器event格式。
更多信息
除了函数计算控制台,您还可通过以下方式配置触发器:
如需对创建的触发器进行修改或删除,请参见触发器管理。