Blog Post

Apps on Azure Blog
4 MIN READ

Open API Extension Support for Azure Functions V1

justinyoo's avatar
justinyoo
Icon for Microsoft rankMicrosoft
Nov 16, 2020

There is an open-source project that generates an Open API document on-the-fly on an Azure Functions app. The open-source project also provides a NuGet package library. This project has been supporting the whole Azure Functions runtimes from v1 since Day 1. Although it does, the v1 runtime has got its own limitation that cannot generate the Open API document automatically. But, as always, there's a workaround. I'm going to show how to generate the Open API document on-the-fly and execute the v1 function through the Azure Function Proxy feature.

 

Legacy V1 Azure Function

 

Generally speaking, many legacy enterprise applications still need Azure Functions v1 runtime due to their reference dependencies. Let's assume that the Azure Functions v1 endpoint looks like the following:

 

    namespace MyV1LegacyFunctionApp
    {
        public static class LoremIpsumHttpTrigger
        {
            [FunctionName("LoremIpsumHttpTrigger")]
            public static async Task Run(
                [HttpTrigger(AuthorizationLevel.Function, "GET", Route = "lorem/ipsum")] HttpRequestMessage req,
                ILogger log)
            {
                var content = new MyReturnObject();
    
                var result = req.CreateResponse(HttpStatusCode.OK, content);
    
                return await Task.FromResult(result).ConfigureAwait(false);        }
        }
    }

 

The v1 runtime has a strong tie to the Newtonsoft.Json package version 9.0.1. Therefore, if the return object of MyReturnObject has a dependency on Newtonsoft.Json v10.0.1 and later, the Open API extension cannot be used.

 

Azure Functions Proxy for Open API Document

 

The Azure Functions Proxy feature comes the rescue! Although it's not a perfect solution, it provides with the same developer experience, which is worth trying. Let's build an Azure Functions app targeting the v3 runtime. The name of the proxy function is MyV1ProxyFunctionApp (line #1). All the rest are set to be the same as the legacy v1 app (line #3-7). However, make sure this is the proxy purpose, meaning it does nothing but returns an OK response (line #10).

 

    namespace MyV1ProxyFunctionApp
    {
        public static class LoremIpsumHttpTrigger
        {
            [FunctionName("LoremIpsumHttpTrigger")]
            public static async Task Run(
                [HttpTrigger(AuthorizationLevel.Function, "GET", Route = "lorem/ipsum")] HttpRequest req,
                ILogger log)
            {
                return await Task.FromResult(new OkResult()).ConfigureAwait(false);
            }
        }
    }

 

Once installed the Open API library, let's add decorators above the FunctionName(...) decorator (line #5-9).

 

    namespace MyV1ProxyFunctionApp
    {
        public static class LoremIpsumHttpTrigger
        {
            [OpenApiOperation(operationId: "getIpsum", tags: new[] { "ipsum" }, Summary = "Gets Ipsum from Lorem", Description = "This gets Ipsum from Lorem.", Visibility = OpenApiVisibilityType.Important)]
            [OpenApiParameter(name: "name", In = ParameterLocation.Query, Required = true, Type = typeof(string), Summary = "Lorem name", Description = "Lorem name", Visibility = OpenApiVisibilityType.Important)]
            [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(MyReturnObject), Summary = "The Ipsum response", Description = "This returns the Ipsum response")]
            [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NotFound, Summary = "Name not found", Description = "Name parameter is not found")]
            [OpenApiResponseWithoutBody(statusCode: HttpStatusCode.BadRequest, Summary = "Invalid Lorem", Description = "Lorem is not valid")]
    
            [FunctionName("LoremIpsumHttpTrigger")]
            public static async Task Run(
                [HttpTrigger(AuthorizationLevel.Function, "GET", Route = "lorem/ipsum")] HttpRequest req,
                ILogger log)
            {
                return await Task.FromResult(new OkResult()).ConfigureAwait(false);
            }
        }
    }

 

All done! Run this proxy app, and you will be able to see the Swagger UI page. As I mentioned above, this app doesn't work but show the UI page. For this app to work, extra work needs to be done.

 

proxies.json to Legacy Azure Functions V1

 

Add the proxies.json file to the root folder. As we added the same endpoint as the legacy function app on purpose (line #6,11), API consumers should have the same developer experience as before except the hostname change. In addition to that, both querystring values and request headers are relayed to the legacy app (line #13-14).

 

    {
        "$schema": "http://json.schemastore.org/proxies",
        "proxies": {
          "DummyOnOff": {
            "matchCondition": {
              "route": "/api/lorem/ipsum",
              "methods": [
                "GET"
              ]
            },
            "backendUri": "https://mylegacyfunctionapp.azurewebsites.net/api/lorem/ipsum",
            "requestOverrides": {
              "backend.request.headers": "{request.headers}",
              "backend.request.querystring": "{request.querystring}"
            }
          }
        }
      }

 

Then update the .csproj file to deploy the proxies.json file together (line #10-12).

 

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <AzureFunctionsVersion>v3</AzureFunctionsVersion>
        ...
      </PropertyGroup>
      ...
      <ItemGroup>
        ...
        <None Update="proxies.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
      </ItemGroup>
      ...
    </Project>

 

All done! Run this proxy function on your local machine or deploy it to Azure, and hit the proxy API endpoint. Then you'll be able to see the Open API document generated on-the-fly and execute the legacy API through the proxy.

 


 

So far, we have created an Azure Functions app using the Azure Functions Proxy feature. It also supports the Open API document generation for the v1 runtime app. The flip-side of this approach costs doubled because all API requests hit the proxy then the legacy. The cost optimisation should be investigated from the enterprise architecture perspective.

This article was originally published on Dev Kimchi.

Updated Nov 16, 2020
Version 2.0
No CommentsBe the first to comment