Skip to content

Add support for isolated Azure Functions #318

@idg10

Description

@idg10

The isolated worked process model available for non-LTS versions of .NET (and currently slated to be the main model even for LTS versions from .NET 8.0 on) introduces some changes to the programming model. Most notably, Http Triggers no longer get access to ASP.NET Core representations of requests or responses. Instead, the Microsoft.Azure.Functions.Worker libraries supply HttpRequestData and HttpResponseData wrapper types, and a functions-specific pipeline model.

This means that neither the original IOpenApiHost<HttpRequest, IActionResult> implementation available when you called services.AddOpenApiActionResultHosting<TContext>()> nor the IOpenApiHost<HttpRequest, IHttpResponseResult> version available with services.AddOpenApiAspNetPipelineHosting<TContext>() is applicable.

We need IOpenApiHost<HttpRequestData, Something>, because the HttpRequest (common to both of the existing implementations) is no longer available. The Something can't be HttpResponseData, even though that is ultimately the response type, because you can't construct an HttpResponseData directly. So we will need to follow a similar pattern to the one we introduced for the direct pipeline mode, so most likely an IHttpResponseDataResult.

We want the usage model to look something like this:

private readonly IOpenApiHost<HttpRequestData, IHttpResponseDataResult> host;

...

[Function("TenancyHost-OpenApiHostRoot")]
public Task<HttpResponseData> RunAsync(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "patch", "post", "put", "delete", Route = "{*path}")]
    HttpRequestData req,
    ExecutionContext executionContext)
{
    return this.host.HandleRequestAsync(req, new { ExecutionContext = executionContext });
}

and HandleRequestAsync here would be an extension method that used the underlying host interface and dealt with getting the HttpRequestResponse from the IHttpResponseDataResult:

// This would be built into Menes
public static async Task<HttpResponseData> HandleRequestAsync(
    this IOpenApiHost<HttpRequestData, IHttpResponseDataResult> host,
    HttpRequestData request,
    object parameters)
{
    IHttpResponseDataResult result = await host.HandleRequestAsync(
        request.Url.AbsolutePath,
        request.Method,
        request,
        parameters);
    return await result.ExecuteResultAsync(request);
}

Our implementation of IOpenApiHost<TRequest, TResponse> is already generic, so to enable this we just need to supply implementations of the various types that is depends on.

We would need an implementation of IOpenApiParameterBuilder<HttpRequestData>. the existing IOpenApiParameterBuilder<HttpRequest> implementation, Menes.Internal.HttpRequestParameterBuilder in the Menes.Hosting.AspNetCore project currently contains a mixture of logic that is specific to HttpRequest and which would be equally applicable to HttpRequestData. We should refactor this so that the common behaviour is in a base class in Menes.Hosting, with the current Menes.Hosting.AspNetCore and a new Menes.Hosting.FunctionsWorker containing just the request-type-specific behaviour.

We would also need an IOpenApiResultBuilder<IHttpResponseDataResult> implementation. This should require slightly less rework, because we already have IOpenApiResultBuilder<IActionResult> and IOpenApiResultBuilder<IHttpResponseResult> implementations (OpenApiActionResultBuilder and OpenApiHttpResponseResultBuilder respectively) that both derive from OpenApiResultBuilder<TResult>. These already split generic behaviour out from response-specific behaviour. (That's because whereas we only had a single input type before, we already had two different result types.)

We would also need two IResponseOutputBuilder<IHttpResponseDataResult> implementations, one for POCOs, and one for OpenApiResults. Again, because this concerns response types, the library is already reasonably well factored—there are equivalent pairs of implementations of this for IActionResult and IHttpResponseResult already, and these pairs derive from a pair of base classes: PocoOutputBuilder<TResult> and OpenApiResultOutputBuilder<TResult>. We should be able to derive two more types from these to define ones that work with IHttpResponseDataResult.

We would also need to ensure that the OpenApiWebHostManager type that provides the ability to host services in-process works with the new approach.

So in summary:

  • IOpenApiParameterBuilder<HttpRequestData>
  • IOpenApiResultBuilder<IHttpResponseDataResult>
  • IResponseOutputBuilder<IHttpResponseDataResult> for POCO
  • IResponseOutputBuilder<IHttpResponseDataResult> for OpenApiResult
  • New PetStore example
  • Ensure in-process testing works

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions