全部产品
阿里云办公

API网关触发函数计算

更新时间:2018-02-09 14:52:02

背景信息

函数计算(Function Compute)是一种事件驱动的服务。函数的执行可以由事件驱动,即当某个事件发生时,触发函数的执行。目前函数计算支持API网关作为事件源,简单说来,当有请求到达已经设置函数计算为后端服务的API网关时,API网关会触发函数的执行,函数计算会将执行结果返回给API网关。

本示例对API网关触发函数计算的使用步骤进行详细介绍,并以运行环境为Java为例,对API网关传入的请求参数进行解析。

通过示例,您将了解

  • 如何使用API网关触发函数计算;
  • 如何在函数中获取API网关传入函数的参数,并将处理结果返回给API网关(以Java运行环境为例)。

本示例分为以下三个步骤

  • 明确API网关和函数计算对接的格式要求(一定要以这个格式,否则互相不认识);
  • 创建服务和需要被API网关触发的函数(已有服务和函数,可跳过此步骤);
  • API网关控制台配置函数计算作为API后端服务。

API网关和函数计算对接的格式要求

API网关调用函数服务时,会将API的相关数据包装为一个Map形式传给函数计算服务,函数计算服务处理后,需要按照返回参数的格式返回statusCodeheadersbody等相关数据,API网关再将函数计算返回的内容映射到statusCodeheaderbody等位置返回给客户端。

API网关的传入参数格式

当以函数计算作为API网关的后端服务时,API网关会把请求参数通过一个固定结构传给函数计算的入参event,函数计算通过如下结构去获取需要的参数,然后进行处理,该结构如下:

  1. {
  2. "path":"api request path",
  3. "httpMethod":"request method name",
  4. "headers":{all headers,including system headers},
  5. "queryParameters":{query parameters},
  6. "pathParameters":{path parameters},
  7. "body":"string of request payload",
  8. "isBase64Encoded":"true|false, indicate if the body is Base64-encode"
  9. }

函数计算的返回参数格式

函数计算需要将输出内容通过如下JSON格式返回给API网关,方便API网关解析。

  1. {
  2. "isBase64Encoded":true|false,
  3. "statusCode":httpStatusCode,
  4. "headers":{response headers},
  5. "body":"..."
  6. }

创建服务和函数

创建服务和函数部分分为两个步骤,本示例以Java Runtime为例

  • 首先需要编写Java代码对API网关传入的参数进行处理,并返回符合格式要求的结果给API网关
  • 创建服务和函数,函数计算提供给我们两种方式创建服务和函数,本示例对两种创建函数的方式分别进行介绍。(如果已有服务就无需重新创建服务了,新建函数就可以)
    • 一种是通过控制台上传代码包的方式创建函数;
    • 另一种是通过命令行工具fcli创建函数。

      温馨提示:这里提供了两种创建函数的方法,您根据个人喜好任选其一即可。

编写函数代码

用户在使用Java编程时,必须要实现一个类,它要实现函数计算预定义的接口,目前有2个预定义的接口可以实现(您任选其一即可):

  • StreamRequestHandler以流的方式接受调用输入(event)和返回执行结果,用户需要从inputStream中读取调用函数时的输入,处理完成后把函数执行结果写入到outputStream中来返回
  • PojoRequestHandler<I, O>通过泛型的方式,用户可以自定义输入和输出的类型,但是它们必须是POJO类型。下面将举例如何使用这个接口API网关触发函数计算的场景更适合使用PojoRequestHandler<I, O>接口,但是本文也提供使用以StreamRequestHandler接口实现的方法

使用PojoRequestHandler<I, O>接口(推荐)

本示例演示了在Java Runtime中,通过实现函数计算预定义的接口PojoRequestHandler,对从API网关传入的参数进行处理,并将结果返回给API网关的过程。PojoRequestHandler<I, O>通过泛型的方式接受调用输入和返回执行结果,用户可以自定义输入和输出的类型,但是它们必须是POJO类型。下面将举例如何使用这个接口通过自定义类ApiRequest传入API网关的参数,函数计算获取参数,对参数进行处理,并通过ApiResponse返回函数计算处理结果的过程。Java Runtime使用请参考Java Runtime

  1. import com.aliyun.fc.runtime.Context;
  2. import com.aliyun.fc.runtime.PojoRequestHandler;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. public class ApiTriggerDemo implements PojoRequestHandler<ApiRequest, ApiResponse> {
  6. public ApiResponse handleRequest(ApiRequest request, Context context) {
  7. // Get ApiRequest info
  8. context.getLogger().info(request.toString());
  9. String path = request.getPath();
  10. String httpMethod = request.getHttpMethod();
  11. String body = request.getBody();
  12. context.getLogger().info("path:" + path);
  13. context.getLogger().info("httpMethod:" + httpMethod);
  14. context.getLogger().info("body:" + body);
  15. // Deal with your own logic here
  16. // ApiResponse example
  17. Map headers = new HashMap();
  18. boolean isBase64Encoded = false;
  19. int statusCode = 200;
  20. String returnBody = "";
  21. return new ApiResponse(headers,isBase64Encoded,statusCode,returnBody);
  22. }
  23. }

两个pojo类,ApiRequest类和ApiResponse类如下。注意pojo类的set()get()方法注意要写全哈

  1. import java.util.Map;
  2. public class ApiRequest {
  3. private String path;
  4. private String httpMethod;
  5. private Map headers;
  6. private Map queryParameters;
  7. private Map pathParameters;
  8. private String body;
  9. private boolean isBase64Encoded;
  10. @Override
  11. public String toString() {
  12. return "Request{" +
  13. "path='" + path + '\'' +
  14. ", httpMethod='" + httpMethod + '\'' +
  15. ", headers=" + headers +
  16. ", queryParameters=" + queryParameters +
  17. ", pathParameters=" + pathParameters +
  18. ", body='" + body + '\'' +
  19. ", isBase64Encoded=" + isBase64Encoded +
  20. '}';
  21. }
  22. public String getPath() {
  23. return path;
  24. }
  25. public void setPath(String path) {
  26. this.path = path;
  27. }
  28. public String getHttpMethod() {
  29. return httpMethod;
  30. }
  31. public void setHttpMethod(String httpMethod) {
  32. this.httpMethod = httpMethod;
  33. }
  34. public Map getHeaders() {
  35. return headers;
  36. }
  37. public void setHeaders(Map headers) {
  38. this.headers = headers;
  39. }
  40. public Map getQueryParameters() {
  41. return queryParameters;
  42. }
  43. public void setQueryParameters(Map queryParameters) {
  44. this.queryParameters = queryParameters;
  45. }
  46. public Map getPathParameters() {
  47. return pathParameters;
  48. }
  49. public void setPathParameters(Map pathParameters) {
  50. this.pathParameters = pathParameters;
  51. }
  52. public String getBody() {
  53. return body;
  54. }
  55. public void setBody(String body) {
  56. this.body = body;
  57. }
  58. public boolean getIsBase64Encoded() {
  59. return this.isBase64Encoded;
  60. }
  61. public void setIsBase64Encoded(boolean base64Encoded) {
  62. this.isBase64Encoded = base64Encoded;
  63. }
  64. }
  1. import java.util.Map;
  2. public class ApiResponse {
  3. private Map headers;
  4. private boolean isBase64Encoded;
  5. private int statusCode;
  6. private String body;
  7. public ApiResponse(Map headers, boolean isBase64Encoded, int statusCode, String body) {
  8. this.headers = headers;
  9. this.isBase64Encoded = isBase64Encoded;
  10. this.statusCode = statusCode;
  11. this.body = body;
  12. }
  13. public Map getHeaders() {
  14. return headers;
  15. }
  16. public void setHeaders(Map headers) {
  17. this.headers = headers;
  18. }
  19. public boolean getIsBase64Encoded() {
  20. return isBase64Encoded;
  21. }
  22. public void setIsBase64Encoded(boolean base64Encoded) {
  23. this.isBase64Encoded = base64Encoded;
  24. }
  25. public int getStatusCode() {
  26. return statusCode;
  27. }
  28. public void setStatusCode(int statusCode) {
  29. this.statusCode = statusCode;
  30. }
  31. public String getBody() {
  32. return body;
  33. }
  34. public void setBody(String body) {
  35. this.body = body;
  36. }
  37. }

pom.xml文件如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>apiTrigger</groupId>
  7. <artifactId>apiTrigger</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <build>
  10. <plugins>
  11. <plugin>
  12. <groupId>org.apache.maven.plugins</groupId>
  13. <artifactId>maven-compiler-plugin</artifactId>
  14. <configuration>
  15. <source>1.8</source>
  16. <target>1.8</target>
  17. </configuration>
  18. </plugin>
  19. </plugins>
  20. </build>
  21. <dependencies>
  22. <dependency>
  23. <groupId>com.aliyun.fc.runtime</groupId>
  24. <artifactId>fc-java-core</artifactId>
  25. <version>1.0.0</version>
  26. </dependency>
  27. </dependencies>
  28. </project>

使用StreamRequestHandler接口

使用StreamRequestHandler接口示例如下,需要将输入的InputStream转换为对应的pojo类,pom文件配置与使用PojoRequestHandler<I, O>接口相同,代码如下

  1. import com.aliyun.fc.runtime.Context;
  2. import com.aliyun.fc.runtime.StreamRequestHandler;
  3. import com.aliyun.fc.runtime.Context;
  4. import com.google.gson.Gson;
  5. import java.io.*;
  6. import java.util.Base64;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. public class ApiTriggerDemo2 implements StreamRequestHandler {
  10. public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
  11. try {
  12. // Convert InputStream to string
  13. BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  14. StringBuffer stringBuffer = new StringBuffer();
  15. String string = "";
  16. while ((string = bufferedReader.readLine()) != null) {
  17. stringBuffer.append(string);
  18. }
  19. String input = stringBuffer.toString();
  20. context.getLogger().info("inputStream: " + input);
  21. Request req = new Gson().fromJson(input, Request.class);
  22. context.getLogger().info("input req: ");
  23. context.getLogger().info(req.toString());
  24. String bodyReq = req.getBody();
  25. Base64.Decoder decoder = Base64.getDecoder();
  26. context.getLogger().info("body: " + new String(decoder.decode(bodyReq)));
  27. // Deal with your own logic here
  28. // construct response
  29. Map headers = new HashMap();
  30. headers.put("x-custom-header", " ");
  31. boolean isBase64Encoded = false;
  32. int statusCode = 200;
  33. Map body = new HashMap();
  34. Response resp = new Response(headers, isBase64Encoded, statusCode, body);
  35. String respJson = new Gson().toJson(resp);
  36. context.getLogger().info("outputStream: " + respJson);
  37. outputStream.write(respJson.getBytes());
  38. } catch (IOException e) {
  39. e.printStackTrace();
  40. } finally {
  41. try {
  42. outputStream.close();
  43. inputStream.close();
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }
  49. class Request {
  50. private String path;
  51. private String httpMethod;
  52. private Map headers;
  53. private Map queryParameters;
  54. private Map pathParameters;
  55. private String body;
  56. private boolean isBase64Encoded;
  57. @Override
  58. public String toString() {
  59. return "Request{" +
  60. "path='" + path + '\'' +
  61. ", httpMethod='" + httpMethod + '\'' +
  62. ", headers=" + headers +
  63. ", queryParameters=" + queryParameters +
  64. ", pathParameters=" + pathParameters +
  65. ", body='" + body + '\'' +
  66. ", isBase64Encoded=" + isBase64Encoded +
  67. '}';
  68. }
  69. public String getBody() {
  70. return body;
  71. }
  72. }
  73. // FC need to return the response to API gateway in the following JSON format
  74. class Response {
  75. private Map headers;
  76. private boolean isBase64Encoded;
  77. private int statusCode;
  78. private Map body;
  79. public Response(Map headers, boolean isBase64Encoded, int statusCode, Map body) {
  80. this.headers = headers;
  81. this.isBase64Encoded = isBase64Encoded;
  82. this.statusCode = statusCode;
  83. this.body = body;
  84. }
  85. }
  86. }

创建服务和函数

打成jar包

Java代码需要打成jar包上传到函数计算。如果您使用IntelliJ IDEA集成开发环境,打包方式如下(可参考IDEA导出可执行jar包)

  • File -> Project Structure -> Artifacts -> + -> Jar -> From modules with dependencies -> 选择函数所在的类 -> Apply -> 确定
  • Build -> Build Artifacts -> 找到对应的Aritifacts -> Build
创建服务和函数

函数计算提供两种方式创建服务和函数,使用控制台创建服务和函数和使用命令行工具fcli创建服务和函数,下面对这两种方式分别进行介绍

1.使用控制台创建服务和函数使用控制台创建服务和函数图文并茂版请参考如何使用控制台,本示例只给出基本操作步骤

  • 创建服务:左侧服务列表 -> + -> 创建服务 -> 填写服务相关内容 -> 确定
  • 创建函数:进入对应服务 -> 左侧函数列表 -> + -> 新建函数 -> 使用空白模板 -> 不创建触发器 -> 输入函数名称 -> 运行环境选择Java8 -> 代码上传方式选择代码包上传 -> 选择刚刚生成的jar包 -> 设置函数入口,入口形式为[package].[class]::[method],例如我打包后的函数入口为ApiTriggerDemo::handleRequest,其他值选择默认即可这样函数就创建好啦

2.使用命令行工具fcli创建服务和函数

  • 创建服务: mks fc-demo,即创建服务,服务名称为fc-demo (在创建服务时可以指定log project等信息,具体内容可参考mks
  • 创建函数:mkf apiTrigger -t java8 -h ApiTriggerDemo::handleRequest -d fcDemo/out/artifacts/apiTrigger_jar (其中apiTrigger为函数名称,-t为指定运行环境,-h指定函数入口,-d指定代码包,此路径为代码包路径相对于fcli所在位置的路径,具体内容可参考mkf)

创建API

  1. API网关控制台分组管理 -> 创建分组,新建一个API分组(已经有API分组可跳过这步)
  2. API列表 -> 新建API,步骤如图所示创建APIAPI P1API P2API P3API P4

通过API网关触发函数计算可参考以函数计算作为 API 网关后端服务

测试触发器

测试测试成功后发布API即可

参考文献

以函数计算作为 API 网关后端服务

That’s all,enjoy it~Any question,可留言,或加入函数计算官方客户群(钉钉群号:11721331)