Azure Functions HTTP 触发器

借助 HTTP 触发器,可以使用 HTTP 请求调用函数。 可以使用 HTTP 触发器生成无服务器 API 和响应 Webhook。

HTTP 触发函数的默认返回值如下:

  • HTTP 204 No Content,在 Functions 2.x 及更高版本中为空主体
  • HTTP 200 OK,在 Functions 1.x 中为空主体

若要修改 HTTP 响应,请配置输出绑定

有关 HTTP 绑定的详细信息,请参阅概述输出绑定参考

提示

如果计划使用 HTTP 或 WebHook 绑定,请制定计划来避免因实例化 HttpClient 不当导致的端口耗尽现象。 有关详细信息,请参阅如何在 Azure Functions 中管理连接

重要

本文使用选项卡来支持多个版本的 Node.js 编程模型。 v4 模型目前处于预览状态,旨在为 JavaScript 和 TypeScript 开发人员提供更为灵活和直观的体验。 在升级指南中详细了解 v3 和 v4 之间的差异。

Azure Functions 支持两种 Python 编程模型。 定义绑定的方式取决于选择的编程模型。

使用 Python v2 编程模型,可以直接在 Python 函数代码中使用修饰器定义绑定。 有关详细信息,请参阅 Python 开发人员指南

本文同时支持两个编程模型。

示例

可以使用以下 C# 模式之一创建 C# 函数:

  • 进程内类库:编译的 C# 函数,该函数在与 Functions 运行时相同的进程中运行。
  • 独立进程类库:编译的 C# 函数,该函数在独立于运行时的进程中运行。 支持在 .NET 5.0 上运行的 C# 函数需要独立的进程。
  • C# 脚本:主要在 Azure 门户中创建 C# 函数时使用。

本文中的代码默认使用 .NET Core 语法,该语法在 Functions 2.x 版及更高版本中使用。 有关 1.x 语法的信息,请参阅 1.x functions 模板

以下例子展示了一个 HTTP 触发器,它使用 .NET Isolated 中的 ASP.NET Core 集成返回了“hello, world”响应作为 IActionResult

[Function("HttpFunction")]
public IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req)
{
    return new OkObjectResult($"Welcome to Azure Functions, {req.Query["name"]}!");
}

下面的示例演示了一个 HTTP 触发器,该触发器将“hello world”响应作为 HttpResponseData 对象返回:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    public static class HttpFunction
    {
        //<docsnippet_http_trigger>
        [Function(nameof(HttpFunction))]
        public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req,
            FunctionContext executionContext)
        {
            //<docsnippet_logging>
            var logger = executionContext.GetLogger(nameof(HttpFunction));
            logger.LogInformation("message logged");
            //</docsnippet_logging>

            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
            response.WriteString("Welcome to .NET isolated worker !!");

            return response;
        }
        //</docsnippet_http_trigger>
    }
}

本部分包含以下示例:

下面的示例演示 HTTP 触发器绑定。

从查询字符串中读取参数

以下示例从查询字符串中读取名为 id 的参数,然后使用该参数构建返回到客户端的 JSON 文档(内容类型为 application/json)。

@FunctionName("TriggerStringGet")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", 
            methods = {HttpMethod.GET}, 
            authLevel = AuthorizationLevel.ANONYMOUS)
        HttpRequestMessage<Optional<String>> request,
        final ExecutionContext context) {

    // Item list
    context.getLogger().info("GET parameters are: " + request.getQueryParameters());

    // Get named parameter
    String id = request.getQueryParameters().getOrDefault("id", "");

    // Convert and display
    if (id.isEmpty()) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
                        .body("Document not found.")
                        .build();
    } 
    else {
        // return JSON from to the client
        // Generate document
        final String name = "fake_name";
        final String jsonDocument = "{\"id\":\"" + id + "\", " + 
                                        "\"description\": \"" + name + "\"}";
        return request.createResponseBuilder(HttpStatus.OK)
                        .header("Content-Type", "application/json")
                        .body(jsonDocument)
                        .build();
    }
}

从 POST 请求中读取正文

以下示例将 POST 请求的正文读取为 String,然后使用该正文构建返回到客户端的 JSON 文档(内容类型为 application/json)。

    @FunctionName("TriggerStringPost")
    public HttpResponseMessage run(
            @HttpTrigger(name = "req", 
              methods = {HttpMethod.POST}, 
              authLevel = AuthorizationLevel.ANONYMOUS)
            HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {

        // Item list
        context.getLogger().info("Request body is: " + request.getBody().orElse(""));

        // Check request body
        if (!request.getBody().isPresent()) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
                          .body("Document not found.")
                          .build();
        } 
        else {
            // return JSON from to the client
            // Generate document
            final String body = request.getBody().get();
            final String jsonDocument = "{\"id\":\"123456\", " + 
                                         "\"description\": \"" + body + "\"}";
            return request.createResponseBuilder(HttpStatus.OK)
                          .header("Content-Type", "application/json")
                          .body(jsonDocument)
                          .build();
        }
    }

从路由中读取参数

此示例读取名为 id 的必选参数,并从路由路径中读取可选参数 name,然后使用这两个参数构建返回到客户端的 JSON 文档(内容类型为 application/json)。 T

@FunctionName("TriggerStringRoute")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", 
            methods = {HttpMethod.GET}, 
            authLevel = AuthorizationLevel.ANONYMOUS,
            route = "trigger/{id}/{name=EMPTY}") // name is optional and defaults to EMPTY
        HttpRequestMessage<Optional<String>> request,
        @BindingName("id") String id,
        @BindingName("name") String name,
        final ExecutionContext context) {

    // Item list
    context.getLogger().info("Route parameters are: " + id);

    // Convert and display
    if (id == null) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
                        .body("Document not found.")
                        .build();
    } 
    else {
        // return JSON from to the client
        // Generate document
        final String jsonDocument = "{\"id\":\"" + id + "\", " + 
                                        "\"description\": \"" + name + "\"}";
        return request.createResponseBuilder(HttpStatus.OK)
                        .header("Content-Type", "application/json")
                        .body(jsonDocument)
                        .build();
    }
}

从 POST 请求中读取 POJO 正文

下面是此示例中引用的 ToDoItem 类的代码:


public class ToDoItem {

  private String id;
  private String description;  

  public ToDoItem(String id, String description) {
    this.id = id;
    this.description = description;
  }

  public String getId() {
    return id;
  }

  public String getDescription() {
    return description;
  }

  @Override
  public String toString() {
    return "ToDoItem={id=" + id + ",description=" + description + "}";
  }
}

此示例读取 POST 请求的正文。 该请求正文会自动反序列化为 ToDoItem 对象,然后以内容类型 application/json 返回到客户端。 当 ToDoItem 参数分配给 HttpMessageResponse.Builder 类的 body 属性时,它会由 Functions 运行时序列化。

@FunctionName("TriggerPojoPost")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", 
            methods = {HttpMethod.POST}, 
            authLevel = AuthorizationLevel.ANONYMOUS)
        HttpRequestMessage<Optional<ToDoItem>> request,
        final ExecutionContext context) {

    // Item list
    context.getLogger().info("Request body is: " + request.getBody().orElse(null));

    // Check request body
    if (!request.getBody().isPresent()) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
                        .body("Document not found.")
                        .build();
    } 
    else {
        // return JSON from to the client
        // Generate document
        final ToDoItem body = request.getBody().get();
        return request.createResponseBuilder(HttpStatus.OK)
                        .header("Content-Type", "application/json")
                        .body(body)
                        .build();
    }
}

以下示例显示了 HTTP 触发的 TypeScript 函数。 该函数在查询字符串或 HTTP 请求的正文中查找 name 参数。

import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';

export async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    context.log(`Http function processed request for url "${request.url}"`);

    const name = request.query.get('name') || (await request.text()) || 'world';

    return { body: `Hello, ${name}!` };
}

app.http('httpTrigger1', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: httpTrigger1,
});

以下示例显示了 HTTP 触发的 JavaScript 函数。 该函数在查询字符串或 HTTP 请求的正文中查找 name 参数。

const { app } = require('@azure/functions');

app.http('httpTrigger1', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        context.log(`Http function processed request for url "${request.url}"`);

        const name = request.query.get('name') || (await request.text()) || 'world';

        return { body: `Hello, ${name}!` };
    },
});

以下示例演示 function.json 文件中的一个触发器绑定和一个 PowerShell 函数。 该函数在查询字符串或 HTTP 请求的正文中查找 name 参数。

{
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    }
  ]
}
using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$name = $Request.Query.Name
if (-not $name) {
    $name = $Request.Body.Name
}

$body = "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."

if ($name) {
    $body = "Hello, $name. This HTTP triggered function executed successfully."
}

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body       = $body
})

以下示例演示了触发器绑定以及使用该绑定的 Python 函数。 该函数在查询字符串或 HTTP 请求的正文中查找 name 参数。 该示例取决于使用的是 v1 还是 v2 Python 编程模型

import azure.functions as func
import logging

app = func.FunctionApp()

@app.function_name(name="HttpTrigger1")
@app.route(route="hello", auth_level=func.AuthLevel.ANONYMOUS)
def test_function(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')
    return func.HttpResponse(
        "This HTTP triggered function executed successfully.",
        status_code=200
        )

特性

进程内独立工作进程 C# 库都使用 HttpTriggerAttribute 来定义触发器绑定。 C# 脚本改用 function.json 配置文件,如 C# 脚本指南中所述。

独立工作进程函数应用中,HttpTriggerAttribute 支持以下参数:

parameters 说明
AuthLevel 确定请求中需要提供的密钥(如果有),以便调用此函数。 有关支持的值,请参阅授权级别
方法 HTTP Methods的数组,该函数将响应此Methods。 如果未指定,该函数将响应所有 HTTP Methods。 请参阅自定义 HTTP 终结点
Route 定义路由模板,控制函数将响应的请求 URL。 如果未提供任何值,则默认值为 <functionname>。 有关详细信息,请参阅自定义 HTTP 终结点

修饰符

仅适用于 Python v2 编程模型。

对于使用修饰器定义的 Python v2 函数,触发器的以下属性在 route 修饰器中定义,这将添加 HttpTrigger 和 HttpOutput 绑定:

properties 说明
route http 终结点的路由。 如果为“无”,则将其设置为函数名称(如果存在)或用户定义的 python 函数名称。
trigger_arg_name HttpRequest 的自变量名称。 默认值为“req”。
binding_arg_name HttpResponse 的自变量名称。 默认值为“$return”。
methods 函数响应的 HTTP 方法的元组。
auth_level 确定请求中需要提供的密钥(如果有),以便调用此函数。

对于使用 function.json 定义的 Python 函数,请参阅“配置”部分。

批注

Java 函数运行时库中,使用支持以下设置的 HttpTrigger 注释:

配置

仅适用于 Python v1 编程模型

下表说明了可以在传递给 app.http() 方法的 options 对象上设置的属性。

properties 说明
authLevel 确定请求中需要提供的密钥(如果有),以便调用此函数。 有关支持的值,请参阅授权级别
methods HTTP Methods的数组,该函数将响应此Methods。 如果未指定,该函数将响应所有 HTTP Methods。 请参阅自定义 HTTP 终结点
route 定义路由模板,控制函数将响应的请求 URL。 如果未提供任何值,则默认值为 <functionname>。 有关详细信息,请参阅自定义 HTTP 终结点

下表说明了在 function.json 文件中设置的触发器配置属性,这些属性因运行时版本而异。

下表解释了在 function.json 文件中设置的绑定配置属性。

“function.json”属性 说明
type 必需 - 必须设置为 httpTrigger
direction 必需 - 必须设置为 in
name 必需 - 在请求或请求正文的函数代码中使用的变量名称。
authLevel 确定请求中需要提供的密钥(如果有),以便调用此函数。 有关支持的值,请参阅授权级别
methods HTTP Methods的数组,该函数将响应此Methods。 如果未指定,该函数将响应所有 HTTP Methods。 请参阅自定义 HTTP 终结点
route 定义路由模板,控制函数将响应的请求 URL。 如果未提供任何值,则默认值为 <functionname>。 有关详细信息,请参阅自定义 HTTP 终结点

使用情况

本部分详细介绍如何配置 HTTP 触发器函数绑定。

HttpTrigger 注释应该应用于以下类型之一的Methods参数:

  • HttpRequestMessage<T>
  • 任意的本机 Java 类型,例如 int、String、byte[]。
  • 使用可选的可为 null 的值。
  • 任何普通旧 Java 对象 (POJO) 类型。

有效负载

触发器输入类型声明为以下类型之一:

类型 描述
HttpRequest 使用此类型要求应用在 .NET Isolated 中配置 ASP.NET Core 集成
这为你提供了对请求对象和整体 HttpContext 的完全访问权限。
HttpRequestData 请求对象的投影。
自定义类型 当请求正文为 JSON 时,运行时将尝试对其进行分析以设置对象属性。

当触发器参数是 HttpRequestDataHttpRequest 时,也可以使用 Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute 将自定义类型绑定到其他参数。 使用此属性需要 Microsoft.Azure.Functions.Worker.Extensions.Http 版本 3.1.0 或更高版本。 请注意,这是与 Microsoft.AspNetCore.Mvc 中的类似属性不同的类型,在使用 ASP.NET Core 集成时,需要完全限定的引用或 using 语句。 以下示例演示如何使用属性仅获取正文内容,同时仍可使用 ASP.NET Core 集成访问完整的 HttpRequest

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute;

namespace AspNetIntegration
{
    public class BodyBindingHttpTrigger
    {
        [Function(nameof(BodyBindingHttpTrigger))]
        public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
            [FromBody] Person person)
        {
            return new OkObjectResult(person);
        }
    }

    public record Person(string Name, int Age);
}

自定义 HTTP 终结点

默认情况下,创建 HTTP 触发器的函数时,可通过以下格式的路由对该函数进行寻址:

http://<APP_NAME>.chinacloudsites.cn/api/<FUNCTION_NAME>

在 HTTP 触发器的输入绑定中,可以使用可选 route 属性自定义此路由。 可以将任何 Web API 路由约束与参数配合使用。

以下函数代码接受路由中的两个参数(categoryid),并同时使用这两个参数写入响应。

[Function("HttpTrigger1")]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, "get", "post",
Route = "products/{category:alpha}/{id:int?}")] HttpRequestData req, string category, int? id,
FunctionContext executionContext)
{
    var logger = executionContext.GetLogger("HttpTrigger1");
    logger.LogInformation("C# HTTP trigger function processed a request.");

    var message = String.Format($"Category: {category}, ID: {id}");
    var response = req.CreateResponse(HttpStatusCode.OK);
    response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
    response.WriteString(message);

    return response;
}

路由参数使用 HttpTrigger 注释的 route 设置进行定义。 以下函数代码接受路由中的两个参数(categoryid),并同时使用这两个参数写入响应。

package com.function;

import java.util.*;
import com.microsoft.azure.functions.annotation.*;
import com.microsoft.azure.functions.*;

public class HttpTriggerJava {
    public HttpResponseMessage<String> HttpTrigger(
            @HttpTrigger(name = "req",
                         methods = {"get"},
                         authLevel = AuthorizationLevel.FUNCTION,
                         route = "products/{category:alpha}/{id:int}") HttpRequestMessage<String> request,
            @BindingName("category") String category,
            @BindingName("id") int id,
            final ExecutionContext context) {

        String message = String.format("Category  %s, ID: %d", category, id);
        return request.createResponseBuilder(HttpStatus.OK).body(message).build();
    }
}

例如,以下 TypeScript 代码使用两个参数(categoryid)定义了 HTTP 触发器的 route 属性。 该示例会从请求中读取参数,并在响应中返回参数的值。

import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';

export async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    const category = request.params.category;
    const id = request.params.id;

    return { body: `Category: ${category}, ID: ${id}` };
}

app.http('httpTrigger1', {
    methods: ['GET'],
    authLevel: 'anonymous',
    route: 'products/{category:alpha}/{id:int?}',
    handler: httpTrigger1,
});

例如,以下 JavaScript 代码使用两个参数(categoryid)定义了 HTTP 触发器的 route 属性。 该示例从请求中读取参数并在响应中返回它们的值。

const { app } = require('@azure/functions');

app.http('httpTrigger1', {
    methods: ['GET'],
    authLevel: 'anonymous',
    route: 'products/{category:alpha}/{id:int?}',
    handler: async (request, context) => {
        const category = request.params.category;
        const id = request.params.id;

        return { body: `Category: ${category}, ID: ${id}` };
    },
});

例如,以下代码使用两个参数(categoryid)定义了 HTTP 触发器的 route 属性:

@app.function_name(name="httpTrigger")
@app.route(route="products/{category:alpha}/{id:int?}")

可将 function.json 文件中声明的路由参数作为 $Request.Params 对象的属性进行访问。

$Category = $Request.Params.category
$Id = $Request.Params.id

$Message = "Category:" + $Category + ", ID: " + $Id

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $Message
})

函数执行上下文是通过声明为 func.HttpRequest 的参数公开的。 此实例允许函数访问数据路由参数、查询字符串值和允许返回 HTTP 响应的Methods。

定义后,可以通过调用 route_params Methods将路由参数提供给函数。

import logging

import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:

    category = req.route_params.get('category')
    id = req.route_params.get('id')
    message = f"Category: {category}, ID: {id}"

    return func.HttpResponse(message)

通过此配置,现在可以通过以下路由对该函数进行寻址,而不是通过原始路由寻址。

http://<APP_NAME>.chinacloudsites.cn/api/products/electronics/357

此配置允许函数代码支持地址中的两个参数“category”和“id” 。有关如何在 URL 中标记化路由参数的详细信息,请参阅 ASP.NET Core 中的路由

默认情况下,所有函数路由的前缀均为 api。 还可以使用 host.json 文件中的 extensions.http.routePrefix 属性自定义或删除前缀。 以下示例通过将空字符串用于 host.json 文件中的前缀删除 api 路由前缀。

{
    "extensions": {
        "http": {
            "routePrefix": ""
        }
    }
}

使用路由参数

定义了函数的 route 模式的路由参数可用于每个绑定。 例如,如果将某个路由定义为 "route": "products/{id}",则表存储绑定可以在绑定配置中使用 {id} 参数的值。

下面的配置演示如何将 {id} 参数传递到绑定的 rowKey

@app.table_input(arg_name="product", table_name="products", 
                 row_key="{id}", partition_key="products",
                 connection="AzureWebJobsStorage")
import { app, HttpRequest, HttpResponseInit, input, InvocationContext } from '@azure/functions';

const tableInput = input.table({
    connection: 'MyStorageConnectionAppSetting',
    partitionKey: 'products',
    tableName: 'products',
    rowKey: '{id}',
});

export async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    return { jsonBody: context.extraInputs.get(tableInput) };
}

app.http('httpTrigger1', {
    methods: ['GET'],
    authLevel: 'anonymous',
    route: 'products/{id}',
    extraInputs: [tableInput],
    handler: httpTrigger1,
});
const { app, input } = require('@azure/functions');

const tableInput = input.table({
    connection: 'MyStorageConnectionAppSetting',
    partitionKey: 'products',
    tableName: 'products',
    rowKey: '{id}',
});

app.http('httpTrigger1', {
    methods: ['GET'],
    authLevel: 'anonymous',
    route: 'products/{id}',
    extraInputs: [tableInput],
    handler: async (request, context) => {
        return { jsonBody: context.extraInputs.get(tableInput) };
    },
});
{
    "type": "table",
    "direction": "in",
    "name": "product",
    "partitionKey": "products",
    "tableName": "products",
    "rowKey": "{id}"
}

使用路由参数时,会自动为函数创建 invoke_URL_template。 你的客户端可以使用 URL 模板来了解在使用 URL 调用函数时需要在 URL 中传递的参数。 在 Azure 门户中导航到某个 HTTP 触发的函数,然后选择“获取函数 URL”。

可以通过用于列出函数获取函数的 Azure 资源管理器 API 以编程方式访问 invoke_URL_template

使用客户端标识

如果函数应用使用应用服务身份验证/授权,则可通过代码查看有关已验证身份的客户端的信息。 此信息以平台注入的请求标头的形式提供。

还可从绑定数据中读取此信息。 此功能仅适用于 2.x 及更高版本的 Functions 运行时。 而且它目前仅可用于 .NET 语言。

有关经身份验证的客户端的信息作为 ClaimsPrincipal 提供,后者作为请求上下文的一部分提供,如以下示例所示:

经过身份验证的用户通过 HTTP 标头提供。

经过身份验证的用户通过 HTTP 标头提供。

授权级别

授权级别是一个字符串值,指示访问函数终结点所需的授权密钥的类型。 对于 HTTP 触发的函数,授权级别可以是下列值之一:

级别值 说明
匿名 无需 API 密钥。 如果未明确设置某个级别,则这是默认值。
函数 特定于函数的 API 密钥是必需的。
admin 需要主密钥。
级别值 说明
匿名 无需 API 密钥。
函数 特定于函数的 API 密钥是必需的。 如果未明确设置某个级别,则这是默认值。
admin 需要主密钥。

函数访问密钥

Functions 允许使用密钥来增大开发期间访问 HTTP 函数终结点的难度。 除非 HTTP 触发的函数中的 HTTP 访问级别设置为 anonymous,否则请求中必须包含 API 访问密钥。

尽管密钥提供默认的安全机制,但可能需要考虑使用其他选项来保护生产环境中的 HTTP 终结点。 例如,在公共应用中分发共享机密通常不是一个好做法。 如果从公共客户端调用函数,则可能需要考虑实施另一种安全机制。 有关详细信息,请参阅在生产环境中保护 HTTP 终结点

续订函数密钥值时,必须手动将更新的密钥值重新分发给调用你的函数的所有客户端。

授权范围(函数级别)

函数级密钥有两个访问权限作用域:

  • 函数:这些密钥仅适用于定义它们的特定函数。 这些密钥用作 API 密钥时,只允许访问该函数。

  • 主机:主机范围的密钥可用于访问函数应用中的所有函数。 这些密钥用作 API 密钥时,可以访问函数应用中的任何函数。

命名每个密钥方便引用,并且在函数和主机级别存在名为“default”的默认密钥。 函数密钥优先于主机密钥。 如果为两个密钥定义的名称相同,则使用函数密钥。

主密钥(管理员级别)

每个函数应用还有一个名为 _master 的管理员级主机密钥。 除了提供对应用中所有函数的主机级的访问权限外,主密钥还提供对运行时 REST API 的管理访问权限。 无法撤消此密钥。 设置 admin 访问级别时,请求必须使用主密钥;使用任何其他密钥会导致访问失败。

注意

由于主密钥在函数应用中授予提升的权限,不应与第三方共享此密钥或在本机客户端应用程序中分发此密钥。 选择管理访问级别时请慎重。

获取密钥

密钥作为 Function App 的一部分存储在 Azure 中,并进行了静态加密。 若要查看密钥,请创建新的密钥或将密钥滚动更新到新值,在 Azure 门户中导航到某个 HTTP 触发的函数,然后选择“函数密钥”。

你还可以管理主机密钥。 在 Azure 门户中导航到函数应用,并选择“应用密钥”。

可以使用 Azure 资源管理器 API 以编程方式获取函数和主机密钥。 存在列出函数密钥列出主机密钥的 API,使用部署槽时,等效的 API 是列出函数密钥槽列出主机密钥槽

你还可以使用创建或更新函数机密创建或更新函数机密槽创建或更新主机机密创建或更新主机机密槽 API 以编程方式创建新的函数和主机密钥。

可以使用删除函数机密删除函数机密槽删除主机机密删除主机机密槽 API 以编程方式删除函数和主机密钥。

你也可以使用旧的密钥管理 API 来获取函数密钥,但建议改用 Azure 资源管理器 API。

API 密钥的授权

大多数 HTTP 触发器模板要求在请求中提供 API 密钥。 因此,HTTP 请求通常类似于以下 URL:

https://<APP_NAME>.chinacloudsites.cn/api/<FUNCTION_NAME>?code=<API_KEY>

可将该密钥包含在名为 code 的查询字符串变量中,如上所示。 也可以将它包含在 x-functions-key HTTP 标头中。 密钥的值可以为任意为函数定义的函数密钥,也可以为任意主机密钥。

可以允许匿名请求,它不需要密钥。 也可要求使用主密钥。 可使用绑定 JSON 中的 authLevel 属性更改默认授权级别。 有关详细信息,请参阅触发器 - 配置

注意

在本地运行函数时,不管指定的授权级别设置为何,都会禁用授权。 发布到 Azure 后,将强制执行触发器中的 authLevel 设置。 在容器中本地运行时,仍然需要使用密钥。

在生产环境中保护 HTTP 终结点

若要全面保护生产环境中的函数终结点,应考虑实现以下函数应用级别的安全选项之一。 在使用这些函数应用级别的安全Methods之一时,应将 HTTP 触发的函数授权级别设置为 anonymous

启用应用服务身份验证/授权

借助应用服务平台可以使用 Azure Active Directory (AAD) 和多个第三方标识提供者对客户端进行身份验证。 可以使用此策略来实现函数的自定义授权规则,并且可以从函数代码处理用户信息。 若要了解详细信息,请参阅 Azure 应用服务中的身份验证和授权以及使用客户端标识

使用 Azure API 管理 (APIM) 对请求进行身份验证

APIM 为传入请求提供了各种 API 安全选项。 若要了解详细信息,请参阅 API 管理身份验证策略。 有了 APIM,可以配置函数应用以接受仅来自 APIM 实例 IP 地址的请求。 若要了解详细信息,请参阅 IP 地址限制

在隔离环境中部署函数应用

Azure 应用服务环境 (ASE) 提供了用于运行函数的专用宿主环境。 ASE 允许配置单个前端网关,可以使用它对所有传入请求进行身份验证。 有关详细信息,请参阅为应用服务环境配置 Web 应用程序防火墙 (WAF)

Webhook

注意

Webhook 模式仅适用于 1.x 版 Functions 运行时。 进行此更改是为了提高 2.x 及更高版本中 HTTP 触发器的性能。

在 1.x 版中,Webhook 模板为 Webhook 有效负载提供了额外的验证。 在 2.x 及更高版本中,基本 HTTP 触发器仍正常工作,且是针对 Webhook 的推荐Methods。

WebHook 类型

如果函数支持 Webhook,则 webHookType 绑定属性指示此类型,该类型还决定支持的有效负载。 Webhook 类型可以是以下值之一:

类型值 说明
genericJson 常规用途 Webhook 终结点,不包含特定提供程序的逻辑。 此设置会将请求限制为仅请求使用 HTTP POST 以及内容类型为 application/json
github 该函数响应 GitHub Webhook。 不要将 authLevel 属性与 GitHub Webhook 配合使用。
slack 该函数响应 Slack Webhook。 不要对 Slack Webhook 使用 authLevel 属性。

设置 webHookType 属性时,另请不要在绑定上设置 methods 属性。

GitHub Webhook

要响应 GitHub webhook,首先请创建包含 HTTP 触发器的函数,并将 webHookType 属性设置为 github。 然后将其 URL 和 API 密钥复制到 GitHub 存储库的“添加 Webhook”页。

Screenshot that shows how to add a webhook for your function.

Slack Webhook

Slack webhook 为用户生成令牌,而非让用户指定它,所以必须使用 Slack 中的令牌配置特定于函数的密钥。 请参阅授权密钥

Webhook 和密钥

Webhook 授权由属于 HTTP 触发器的 webhook 接收器组件处理,其机制因 webhook 类型而异。 每种机制都依赖于一个密钥。 默认情况下,使用名为“default”的函数密钥。 要使用其他密钥,请将 webhook 提供程序配置为使用以下方式之一的请求发送密钥名称:

  • 查询字符串:提供程序通过 clientid 查询字符串参数(例如,https://<APP_NAME>.chinacloudsites.cn/api/<FUNCTION_NAME>?clientid=<KEY_NAME>)传递密钥名称。
  • 请求标头:提供程序通过 x-functions-clientid 标头传递密钥名称。

内容类型

将二进制文件和窗体数据传递给非 C# 函数需要使用适当的 content-type 标头。 支持的内容类型包括 octet-stream(适用于二进制数据)和多部分类型

已知问题

在非 C# 函数中,使用 content-type image/jpeg 发送的请求会导致向函数传递 string 值。 在这种情况下,可以手动将 string 值转换为字节数组以访问原始二进制数据。

限制

HTTP 请求长度限制为 100 MB(104,857,600 字节),并且 URL 长度限制为 4 KB(4,096 字节)。 这些限制由运行时的 Web.config 文件httpRuntime 元素指定。

如果使用 HTTP 触发器的函数未在 230 秒内完成,Azure 负载均衡器将超时并返回 HTTP 502 错误。 该函数将继续运行,但将无法返回 HTTP 响应。 对于长时间运行的函数,我们建议你遵循异步模式,并返回可以 ping 通请求状态的位置。 有关函数可以运行多长时间的信息,请参阅缩放和托管 - 消耗计划

后续步骤