1

I have a project in Visual Studio using the "Azure Functions" template and the "Http trigger with OpenApi" function, and want to use an OpenApiRequestBodyAttribute to indicate that the function requires a multipart/form-data request, where the "server" parameter is a JSON object, and the "file" parameter is some binary data. Unfortunately, the documentation on the Microsoft.OpenApi library seems pretty sparse, and I haven't been able to find any examples that do exactly what I'm trying to.

So far, this was the best I could come up with:

public class SFTPEndpoints
{
    private readonly ILogger<SFTPEndpoints> _logger;

    public SFTPEndpoints(ILogger<SFTPEndpoints> log)
    {
        _logger = log;
    }

    public record Server(string Url, string Username, string Password, int Port, string Path);
    public record ServerWithFileBytes(Server Server, byte[] File);

    [FunctionName("sftp-string-upload")]
    [OpenApiOperation(operationId: "sftp-string-upload", tags: new[] { "sftp" })]
    [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
    [OpenApiRequestBody("multipart/form-data", typeof(ServerWithFileBytes), Description = "Upload a file to the SFTP Server.")]
    [OpenApiResponseWithoutBody(HttpStatusCode.OK, Summary = "The file was uploaded successfully.")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req)
    {
        _logger.LogInformation("C# HTTP trigger function processed a request.");

        await Console.Out.WriteLineAsync(req.ToString());

        return new OkObjectResult(JsonConvert.SerializeObject(req));
    }
}

This gets me close, and renders the following at the localhost/api/swagger/ui endpoint: Auto-generated Swagger Doc

But when I click the "Try it out" button, the text box for the "server" property is blank. I know from writing the doc for this by hand that when it's configured properly, the box should be pre-populated with the schema for the Server record. The problem is, I don't know how to look at the actual YAML (or JSON?) spec the library is creating from this code, so I'm not sure how to even verify whether the issue is with the UI endpoint, the spec generator, or my annotations. I also tried looking at the OpenApi endpoint, but that always returns an "Invalid OpenApi Version" error no matter what version I specify (in this case localhost/api/openapi/3.0):

Trying to visit localhost/api/openapi/3.0 causes error

If it makes any difference, I've overridden the DefaultOpenApiConfigurationOptions class to force OpenApi3, because without that, the request body would show up as completely blank.

internal class OpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
{
    public override OpenApiVersionType OpenApiVersion { get; set; } = OpenApiVersionType.V3;
}

Here's the spec I wrote by hand before I was cursed with trying to do this within the function code itself:

openapi: 3.0.3
info:
  version: 1.0.0
  title: FTP Server API
  description:
    API methods for interacting with SFTP and FTPS file servers.
servers:
  - url: example.com/api
paths:
  /sftp/upload/file:
    put:
      summary: Upload a file to the SFTP server.
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                ServerData:
                  $ref: "#/components/schemas/Server"
                File:
                  type: string
                  format: binary
      responses:
        200:
          description: File uploaded successfully.
components:
  schemas:
    Server:
      type: object
      properties:
        Url:
          type: string
          description: Can be a URL or an IP.
          example: ftp.example.com
        Username:
          type: string
        Password:
          type: string
          format: password
        Port:
          type: integer
        Path:
          type: string
    ServerWithFile:
      allOf:
        - $ref: "#/components/schemas/Server"
        - type: object
          properties:
            Filename:
              type: string
            File:
              type: string
              format: byte

If someone knows either how to fix my annotations or even just look at the spec that the library generates, that would be really helpful. Thanks!

Caleb Keller
  • 553
  • 3
  • 19
  • 1
    If you look at [the source](https://github.com/Azure/azure-functions-openapi-extension/blob/main/src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Extensions/OpenApiDocumentExtensions.cs) for `serialize`, you can see that the version is a switch on an enum. So the version must be either 0 (for 2.0) or 1 (for 3.0). If you go to `/api/openapi/0` you should get the open api spec according to version 2.0. If you get a 404 doing this, you might have to write "`/api/openapi/0.0`". – Flux May 25 '23 at 14:10
  • 1
    If you look at [the source for the actual function](https://github.com/Azure/azure-functions-openapi-extension/blob/c37b2873ea8d45a16a3521caa827208adc114098/src/Microsoft.Azure.WebJobs.Extensions.OpenApi/OpenApiTriggerFunctions.cs#LL110C29-L110C71), you can see how it handles the version parameter. It passes it to a method called `GetOpenApiSpecVersion`, which tries to parse the parameter and return the appropriate enum. If you look closer at `GetOpenApiSpecVersion`, you can see that `v2` (or `v2.0`) is also acceptable as the parameter value. – Flux May 25 '23 at 14:24
  • Ahh, that helps a lot! Seems strange that the enum values are used instead of the actual OpenAPI version, but at least it works. Thanks! – Caleb Keller May 31 '23 at 13:25

0 Answers0