
# 构建程序
#### 创建API分组
创建函数及添加事件源之前，需要先创建一个API分组，API分组是API的管理单元，用来存放API。
![](https://support.huaweicloud.com/bestpractice-functiongraph/public_sys-resources/note_3.0-zh-cn.png)
您需要拥有一个APIG实例后才能开启API网关服务相关功能，具体请参见[购买实例](https://support.huaweicloud.com/usermanual-apig/apig_03_0037.html)。
1. 登录APIG控制台，在左侧导航栏选择"API管理 \> API分组"，单击"创建API分组"。
2. 选择直接创建，设置以下分组信息，完成后单击"确定"创建分组。 
   - 分组名称：输入您自定义的分组名称，例如APIGroup_test。
   
   - 描述：输入对分组的描述。
   
   
   
   
 
 #### 创建自定义认证函数
前端自定义认证指APIG利用校验函数对收到的API请求进行安全认证，如果您想要使用自己的认证系统对API的访问进行认证鉴权，您可以在API管理中创建一个前端自定义认证来实现此功能。您需要先在FunctionGraph创建一个函数，通过函数定义您所需的认证信息，函数创建成功后，即可对API网关中的API进行认证鉴权。
本示例以Header中的请求参数：event\["headers"\]，为例进行演示。请求参数详细说明请参见[请求参数代码定义示例]。
1. 在服务控制台左侧导航栏，选择"计算 \> 函数工作流"，进入函数工作流控制台后，在左侧导航栏选择"函数 \> 函数列表"，进入函数列表界面。
2. 单击"创建函数"，进入创建函数流程。
3. 填写函数配置信息，完成后单击"创建函数"。 
   - 模板：选择"使用空白模板"。
   
   - 函数类型：事件函数。
   
   - 函数名称：输入您自定义的函数名称，例如：apig-test。
   
   - 委托名称：选择"未使用任何委托"。
   
   - 运行时语言：选择"Python 2.7"。
   
   
   
   
4. 进入函数详情页，在"代码"页签，进行代码在线编辑，复制[Header中的请求参数定义代码示例]中的代码并单击"部署"，更新函数。
5. 配置测试事件，测试用于前端自定义认证的函数。单击"配置测试事件"，选择事件模板。根据实际情况修改后保存测试模板(本示例在"headers"中添加"auth":"abc"），完成后单击"创建"。 
   图1配置测试事件   
   ![](https://support.huaweicloud.com/bestpractice-functiongraph/zh-cn_image_0000002165305729.png "点击放大")
   
   
   
6. 单击"测试"，执行结果为"成功"时，表示自定义认证函数创建成功。 
   图2查看执行结果   
   ![](https://support.huaweicloud.com/bestpractice-functiongraph/zh-cn_image_0000002165184169.png "点击放大")
   
   
 
#### 创建自定义认证
在APIG中创建自定义认证，对接前端自定义认证的函数。
1. 在服务控制台左侧导航栏，选择"应用中间件 \> API网关"登录APIG控制台，在左侧导航栏选择"API管理 \> API策略"，在"自定义认证"页签下，单击"创建自定义认证"，弹出"创建自定义认证"对话框。
2. 配置自定义认证基础信息，如下图所示。 
   - 认证名称：输入您自定义的名称，例如Authorizer_test。
   
   - 类型：选择"前端"。
   
   - 函数地址：请选择用于前端自定义认证的函数apig-test。
     图3创建自定义认证   
     ![](https://support.huaweicloud.com/bestpractice-functiongraph/zh-cn_image_0000002129786234.png "点击放大") 
   
   
   
   
3. 完成后单击"确定"，完成自定义认证的创建。
 
#### 创建后端业务函数
API网关（APIG）支持选择FunctionGraph作为后端服务类型，当请求设置函数工作流为后端服务的API时，API网关会触发相应的函数，函数工作流会将执行结果返回给API网关（APIG）。
1. 创建函数方法与上述[创建自定义认证函数]相同，只需修改函数名称，避免名称重复。
2. 在函数详情页的"代码"页签，进行代码在线编辑，并传入如下所示的代码，完成后单击"部署"，更新函数。 
   ```
   # -*- coding:utf-8 -*-
   import json
   def handler (event, context):
       body = "<html><title>Functiongraph Demo</title><body><p>Hello, FunctionGraph!</p></body></html>"
       print(body)
       return {
           "statusCode":200,
           "body":body,
           "headers": {
               "Content-Type": "text/html",
           },        
           "isBase64Encoded": False
       }
   ```
   
   
 
 #### 请求参数代码定义示例
在FunctionGraph中开发函数，以python2.7语言为例，函数代码需要满足如下条件。
函数有明确的接口定义，如下所示：
**def handler (event, context)**
- 入口函数名（handler）：入口函数名称，需和函数执行入口处用户自定义的入口函数名称一致。
- 执行事件（event）： 函数执行界面由用户输入的执行事件参数， 格式为JSON对象。
- 上下文环境（Context）：Runtime提供的函数执行上下文，其接口定义在[SDK接口](https://support.huaweicloud.com/devg-functiongraph/functiongraph_02_0420.html#section2)说明。
 
**执行事件（event）支持三种请求参数定义，格式为：**
- Header中的请求参数：event\["headers"\]\["参数名"\]
- Query中的请求参数：event\["queryStringParameters"\]\["参数名"\]
- 您自定义的用户数据：event\["user_data"\]
 
**函数代码获取的三种请求参数与API网关自定义认证中的参数关系如下所示：**
- Header中的请求参数：对应自定义认证中参数位置为Header的身份来源，其参数值在您调用使用该前端自定义认证的API时传入
- Query中的请求参数：对应自定义认证中参数位置为Query的身份来源，其参数值在您调用使用该前端自定义认证的API时传入
- 您自定义的用户数据：对应自定义认证中的用户数据，其参数值在您创建自定义认证时输入
- 函数的返回值不能大于1M，必须满足如下格式：
  ```
  {     "statusCode":200,
        "body": "{\"status\": \"allow\", \"context\": {\"user\": \"abc\"}}" 
   }
  ```
  
 
其中，body字段的内容为字符串格式，json解码之后为：
```
{
"status": "allow/deny",
"context": {
"user": "abc"
}
}
```
"status"字段为必选，用于标识认证结果。只支持"allow"或"deny"，"allow"表示认证成功，"deny"表示认证失败。
"context"字段为可选，只支持字符串类型键值对，键值不支持JSON对象或数组。
context中的数据为您自定义的字段，认证通过后作为认证参数映射到API网关后端参数中，其中context中的参数名称与系统参数名称必须完全一致，且区分大小写，context中的参数名称必须以英文字母开头，支持英文大小写字母、数字、下划线和中划线，且长度为1 \~ 32个字符。
**Header中的请求参数定义代码示例**：
```
# -*- coding:utf-8 -*-
import json
def handler(event, context):
    if event["headers"].get("auth")=='abc':
        resp = {
            'statusCode': 200,
            'body': json.dumps({
                "status":"allow",
                "context":{
                    "user":"success"
                }
            })
        }
    else:
        resp = {
            'statusCode': 200,
            'body': json.dumps({
                "status":"deny",
            })   
        }
    return json.dumps(resp)
```
**Query中的请求参数定义代码示例**：
```
# -*- coding:utf-8 -*-
import json
def handler(event, context):
    if event["queryStringParameters"].get("test")=='abc':
        resp = {
            'statusCode': 200,
            'body': json.dumps({
                "status":"allow",
                "context":{
                    "user":"abcd"
                }
            })
        }
    else:
        resp = {
            'statusCode': 200,
            'body': json.dumps({
                "status":"deny",
            })   
        }
    return json.dumps(resp)
```
**用户数据定义代码示例**：
```
# -*- coding:utf-8 -*-
import json
def handler(event, context):
    if event.get("user_data")=='abc':
        resp = {
            'statusCode': 200,
            'body': json.dumps({
                "status":"allow",
                "context":{
                    "user":"abcd"
                }
            })
        }
    else:
        resp = {
            'statusCode': 200,
            'body': json.dumps({
                "status":"deny",
            })   
        }
    return json.dumps(resp)
```
