Hosting ASP.NET Core API inside Azure Functions (without TestServer)

Nikolay Mozgovoy

I do believe that serverless hosting is capable in covering most of the needs for the most of the web applications. Serverless architecture is great in relieving us from pain of managing the computation resources and infrastructure, being surprisingly cost-efficient at the same time.

If you are an ASP.NET Core developer, I do expect that you might be well interested in an option of serverless hosting of your applications in Azure especially having 1,000,000 request executions per month for free.

Azure platform offers developers Azure functions as a serverless compute platform. While initial purpose expected from Microsoft was to provide a quick and lightweight solution to manage various automation tasks, Azure Functions are still capable of running fully-functional ASP.NET Core applications. At the moment, such option is not offered by existing templates, but you can apply the following instructions and samples to host your existing application inside serverless Azure Function. If you wish to skip the explanation, you may proceed straightly to the sample at GitHub.

Steps

At a high level we need to have 2 components:

  • ASP.NET Core application we want to be hosted in serverless platform
  • Azure function application, in which we are going to host the existing app

During the following steps we will create and configure both applications:

  1. Get your development environment ready according to official instructions.
  2. Create Web API and Azure function projects (the following commands were executed in PowerShell, but I assume they will work out in Unix shell just fine):
  1. # Create ASP.NET Core Web API project
  2. dotnet new webapi -n "WebApp"
  3. # Create Azure Function project
  4. func init "FuncApp" --worker-runtime dotnet
  5. # Proceed to its folder
  6. cd FuncApp
  7. # Create new HTTP Trigger function
  8. func new --name "WebAppProxy" --language "C#" --template HttpTrigger
  9. # At this point we need to ensure that both projects (WebApp and FuncApp) # target the same version of .NET Core
  10. # so please open their *.csproj files and check the following tag:
  11. # <TargetFramework>netcoreapp2.2</TargetFramework>
  12. # Reference ASP.NET Core package
  13. dotnet add package "Microsoft.AspNetCore.App"
  14. # Reference Web API project
  15. dotnet add reference "../WebApp/"

 

  1. Now we need to make a small adjustment for Web API project in Startup.cs:
  1. /* This method gets called by the runtime.
  2.    Use this method to add services to the container. */
  3. public void ConfigureServices(IServiceCollection services)
  4. {
  5.    services
  6. /* Logging is required */
  7.       .AddLogging()
  8.         .AddMvc()
  9. /* Explicitly add WebApp assembly as application part
  10.    This is required because WebApp isn't executing assembly
  11.    when being hosted as Azure Function */
  12.       .AddApplicationPart(Assembly.Load("WebApp"));
  13. }

 

  1. At last it’s time to implement Azure Function in WebAppProxy.cs so that it delegates request handling to WebApp:
  1. [FunctionName(nameof(WebAppProxy))]
  2. public static async Task<IActionResult> Run(
  3.         [HttpTrigger(
  4.                 AuthorizationLevel.Anonymous,
  5.                 "get", "post", "put", "patch",
  6.                 Route = "{*any}")]
  7.         HttpRequest req,
  8.         ExecutionContext context,
  9.         ILogger log)
  10. {
  11.         log.LogInformation("C# HTTP trigger function processed a request.");
  12.  
  13.         var configRoot = new ConfigurationBuilder()
  14.                 .SetBasePath(context.FunctionAppDirectory)
  15.                 .AddEnvironmentVariables()
  16.                 .Build();
  17.  
  18.         var hostingEnvironment = new HostingEnvironment()
  19.         {
  20.                 ContentRootPath = context.FunctionAppDirectory
  21.         };
  22.  
  23.         /* Add required services into DI container */
  24.         var config = configRoot.GetWebJobsRootConfiguration();
  25.         var services = new ServiceCollection();
  26.         services.AddSingleton<DiagnosticSource>(new DiagnosticListener("Microsoft.AspNetCore"));
  27.         services.AddSingleton<ObjectPoolProvider>(new DefaultObjectPoolProvider());
  28.         services.AddSingleton<IHostingEnvironment>(hostingEnvironment);
  29.         services.AddSingleton<IConfiguration>(config);
  30.  
  31.         /* Instantiate standard ASP.NET Core Startup class */
  32.         var startup = new Startup(config);
  33.         /* Add web app services into DI container */
  34.         startup.ConfigureServices(services);
  35.         /* Initialize DI container */
  36.         var serviceProvider = services.BuildServiceProvider();
  37.         /* Initialize Application builder */
  38.         var appBuilder = new ApplicationBuilder(
  39.            serviceProvider, new FeatureCollection());
  40.         /* Configure the HTTP request pipeline */
  41.         startup.Configure(appBuilder, hostingEnvironment);
  42.         /* Build request handling function */
  43.         var requestHandler = appBuilder.Build();
  44.         /* Set DI container for HTTP Context */
  45.         req.HttpContext.RequestServices = serviceProvider;
  46.         /* Handle HTTP request */
  47.         await requestHandler(req.HttpContext);
  48.         /* This dummy result does nothing.
  49.          HTTP response is already set by requestHandler */
  50.         return new EmptyResult();
  51. }

 

  1. Finally, it’s time to run Azure function:
  1. func settings add FUNCTIONS_WORKER_RUNTIME dotnet
  2. func host start -build

 

At this point I do expect you will receive test data calling the following endpoint: http://localhost:7071/api/values

Closing

I must confess that the described approach isn’t really straightforward, and I had to spend some time to get it working. I hope that it will be useful for developers looking for the cheapest way of application hosting in Azure, until Microsoft offers a cleaner approach.

References

Official Azure Functions documentation:

https://docs.microsoft.com/en-us/azure/azure-functions/

Sample source code:

https://github.com/NicklausBrain/serverless-core-api

Need an innovative and reliable IT consultant?

Let's connect

Contact us

Thank you for reaching out to Sigma Software! Please fill the form below. Our team will contact you shortly.