While Azure Functions allows you to build very lightweight HTTP WebApis out of the box, the HTTP binding is very limited and sometimes we need access to the HTTP pipeline. In this post I show you how you can host a ASP.NET Core WebApi inside of a Azure Function.

Prerequisites

To get this working you need

  • Azure Functions V2
  • Latest Azure Function Core Tools (install via npm, using npm i -g azure-functions-core-tools@core)
  • The ASP.NET Core WebApi needs to target ASP.NET Core 2.0.1

How does it work

To get the WebApi running in Azure Functions we are using the ASP.NET Core Testhost. Then we need a generic HTTP Trigger that passes the request to the Testhost and passes the response from the Testhost back to the client.

Setup Testhost

Since the instantiation of the Testhost takes some time we should do this only once. In our function we create a static constructor which instantiates and configure the Testhost.

private static readonly HttpClient _client;

static Function()
{
    var functionPath = new FileInfo(typeof(Function).Assembly.Location).Directory.Parent.FullName;
    Directory.SetCurrentDirectory(functionPath);
    var server = CreateServer(functionPath);
    _client = server.CreateClient();
}

private static TestServer CreateServer(string functionPath) =>
    new TestServer(WebHost
        .CreateDefaultBuilder()
        .ConfigureAppConfiguration((builderContext, config) =>
        {
            config
                .SetBasePath(functionPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{builderContext.HostingEnvironment.EnvironmentName}.json",
                    optional: true, reloadOnChange: true)
                .AddEnvironmentVariables();
        })
        .UseStartup<Startup>()); //Use the startup class of your WebApi project.

Preparing the WebApi

Usually the WebApi project targets netcoreapp2.0 but the Functions project targets netstandard2.0 you can not reference the WebApi project in the Functions Project. Therefor we need to change the WebApi project to target netstandard2.0. Unfortunately we are then no longer able to use the Microsoft.AspNetCore.All Meta-Package. This needs to be removed and we need to reference each AspNetCore package separately. The proj-file of your WebApi project should look like this.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <OutputType>Library</OutputType>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.1" />
  </ItemGroup>
</Project>

The proxy function

We can now create a function with a HTTP trigger that receives the request and passes it to the Testhost.

[FunctionName("Proxy")]
public static Task<HttpResponseMessage> Run([HttpTrigger(
        AuthorizationLevel.Anonymous,
        "get", "post", "put", "patch", "options",
        Route = "{*x:regex(^(?!admin|debug|monitoring).*$)}")] HttpRequestMessage req)
{
    return _client.SendAsync(req);
}

The HTTP trigger is configured to match all urls, except /admin, /debug, /monitoring since those are internal urls of the function host. We pass the request to the Test host and return the response of the Testhost to the caller. The authorization level should be anonymous, since we can now configure and use the authentication in the WebApi.

Remove the API route prefix

HTTP trigger routes are usually prefixed with /api. If we want to remove this we can configure this in the host.json.

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

Conclusion

With the Testhost we can host and run a WebApi inside of an Azure Function. This enables to benefit of the best of two worlds. The pricing and scaling of Azure Functions consumption plan and the flexibility of ASP.NET Core!

You can find a full working example at GitHub.