如何延迟读取触发 HTTP 请求的正文,直到稍后在 Azure 函数中?

Posted

技术标签:

【中文标题】如何延迟读取触发 HTTP 请求的正文,直到稍后在 Azure 函数中?【英文标题】:How to delay reading the body of the trigger HTTP request until later in an Azure Function? 【发布时间】:2020-04-12 05:22:08 【问题描述】:

我正在使用 Azure Functions 处理以 base64 编码并在 Json 中传递的大型 (>20MB) 文件。

在读取和解析 JSON 正文之前,我需要通过 HTTP 请求标头中传递的 API 密钥对客户端进行身份验证。

分析日志,似乎在执行函数之前就读取了整个 JSON。

有没有办法可以延迟读取 JSON 正文,直到我对用户进行身份验证?

编辑:

函数声明如下:

public static class V1Functions


    [FunctionName("V1MyFunction")]
    public static async Task<IActionResult> MyFunction(
        [HttpTriggerAttribute(AuthorizationLevel.Anonymous, "post", Route = "v1/my_function")] HttpRequest request,
        ILogger logger)
    
       //...
    

【问题讨论】:

我知道 JSON 中 20MB 的 base64 编码文件远非一个好习惯,但 ::shrug:: 这些是要求。 你能以任何方式修改客户端吗?例如,在带有有效负载的 POST 之前向您发送带有 API 密钥的 HEAD 请求。然后,您可以返回他们需要包含在 POST 中的质询标头。 实际上,既然你在问,你可能不能:)。在将请求正文读入请求对象(然后将其传递到您的代码中)后,您的功能代码将触发。我会说放弃它并切换到像 ASP.NET Core 这样的原始东西。 @evilSnobu 你知道函数过滤器(例如 OnExecutingAsync)是否有帮助吗? 我不知道他们的存在。看起来很有希望。我会在这里提出一个问题并询问 Azure Functions 的开发人员 - github.com/Azure/azure-functions-host 【参考方案1】:

我遇到了类似的问题,传入的请求必须在执行之前进行身份验证。然而,我并不真正关心内容的大小(它不应该达到例如~20MB)。因此,我不确定这种方法是否真的符合您的需求。

正如您在评论中所述,OnExecutingAsync 可用于将要执行的代码放置在函数本身之前。问题是,OnExecutingAsync 只是一个任务;你不能真正从中返回任何东西 - 如果可以这样做会很好,因为它可以让我们返回例如“Unauhorized”,通常在谈论 http 请求时就是这种情况。

在任何情况下,无论它是否符合您的要求,这里是:在我看来,请求将内容作为流保存,并且 afaik 它还没有被 http 触发管道处理,至少直到你打电话给例如ReadAsStringAsync 或类似的东西。

使用实现IFunctionInvocationFilter 的 BaseController,类似于:

注意:截至 2019 年 12 月 29 日,IFunctionInvocationFilter 仍处于预览阶段。

internal abstract class BaseController : IFunctionInvocationFilter

    protected bool _IsAuthenticated = false;

    private IAuthenticationService _AuthenticationService;

    protected BaseController(IAuthenticationService authenticationService)
    
        _AuthenticationService = authenticationService;
    

    public virtual async Task OnExecutedAsync(FunctionExecutedContext executedContext, CancellationToken cancellationToken)
    
        _IsAuthenticated = false;
    

    public virtual async Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
    
        // This part is a tad flimsy, but I never managed to find a better way of retrieving the header values
        // You could probably investigate the FunctionExecutingContext a tad more and see if you can come up with something better

        if (executingContext.Arguments.TryGetValue("request", out var request) && request is HttpRequest httpRequest)
            _IsAuthenticated = _AuthenticationService.Authenticate(httpRequest.Headers);
        else
            _IsAuthenticated = false;
    

    protected async Task<IActionResult> DoAuthenticated(Func<Task<IActionResult>> action)
    
        return !IsAuthenticated ? Unauthenticated() : await action();
    

    protected virtual IActionResult Unauthenticated()
    
        var someErrorHandlingWithSomeModelAsBody = ...
        return new UnauthorizedObjectResult(someErrorHandlingWithSomeModelAsBody);
    

AuthenticationService能够判断头部中的key是否有效。

您可能已经注意到依赖注入的使用(这正是我制作的函数应用程序的实现方式),您可能可以轻松地纠正这一点,根据需要使事物变为静态。

示例控制器类似于:

public class SampleController : BaseController

    private readonly ISampleService _SampleService;

    public SampleController(ISampleService sampleService, IAuthenticationService authenticationService) : base(authenticationService)
    
        _SampleService = sampleService;
    

    [FunctionName(nameof(SampleFunction))]
    public async Task<IActionResult> SampleFunction(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/my_function")] HttpRequest request,
        ILogger log)
    
        try
        
            return await DoAuthenticated(async () =>
            
                var largeFileAsString = await request.Content.ReadAsStringAsync();

                return await _SampleService.HandleLargeFile(largeFileAsString);
            
        
        catch (Exception e)
        
            // If you need exception handling...
        
    

每当SampleFunction被触发时,都会按以下顺序执行:

    OnExecutingAsync-body SampleFunction-body - 只有在 OnExecutingAsync 期间验证实际上成功时才会执行该 func OnExecutedAsync-body

问题是,即使身份验证失败,该函数是否仍能处理您所说的整个 20MB 的 JSON 内容。如果您对此进行测试,我个人实际上会对结果感兴趣。

【讨论】:

【参考方案2】:

您可以使用 Azure 应用服务内置的身份验证和授权支持(easy auth)对用户进行身份验证。有关 Azure 应用服务轻松身份验证的详细信息,请参阅 here。

Azure 函数基于 Azure 应用服务,因此也为 Azure 函数启用。

This section 表示它是如何工作的:

每个传入的 HTTP 请求在被处理之前都会经过它 您的应用程序代码。

我认为这是您正在寻找的身份验证方式:在用户通过身份验证之前不处理代码中的请求数据。 This blog将有助于您使用Azure功能和Easy Auth。

希望对你有帮助。

【讨论】:

他不能。您错过了他从标头中读取 API 密钥的部分。

以上是关于如何延迟读取触发 HTTP 请求的正文,直到稍后在 Azure 函数中?的主要内容,如果未能解决你的问题,请参考以下文章

如何延迟加载反应应用程序直到触发操作?

Azure Function App HTTP 触发来自请求正文的 CosmosDB 输入查询

如何使用 HTTP 请求触发 Firebase 函数以从多个节点读取数据?

如何使用 Golang 的 net/http 包读取流响应正文?

如何从状态为 101 切换协议的响应中读取正文

如何确定 HTTP Servlet 请求的内容类型?