How to configure OpenApiRequestBody in Azure Function?

Viewed 5126

I want to decorate my azure c# function with OpenApi annotations. The function accept JSON schema as parameters. How to mention that in the annotation.

Want to know how to configure below annotation

[OpenApiRequestBody(contentType: "json", bodyType: typeof(System.Text.Json.JsonDocument), Description = "Parameters",Example =typeof(Parameters))]

public class ModifyOrder
    {
        [FunctionName("ModifyOrder")]
        [OpenApiOperation(operationId: "run", tags: new[] { "Modify Order" })]
        [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
        [OpenApiRequestBody(contentType: "json", bodyType: typeof(System.Text.Json.JsonDocument), Description = "Parameters",Example =typeof(Parameters))]
        [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")]
        public static async Task<IActionResult> run(
            [HttpTrigger(AuthorizationLevel.Function, "put", Route = null )] HttpRequest req,
            ILogger log)
        {
            log.LogInformation($"C# HTTP trigger function processed a request.");

            string ordernumber;

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            ordernumber = data?.orderno;

            string responseMessage = $"Order:{ordernumber}";
            return new OkObjectResult(responseMessage);
        }

    }

    [OpenApiExample(typeof(Parameters))]
    public class Parameters 
    {
        /// <summary>The id of the customer in the context. This is also called payer, sub_account_id.</summary>
        [Newtonsoft.Json.JsonProperty("customerId", Required = Newtonsoft.Json.Required.Always)]
        [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
        public string CustomerId { get; set; }
        /// <summary>The order number. Used to uniquely identify a group of order lines.</summary>
        [Newtonsoft.Json.JsonProperty("orderNumber", Required = Newtonsoft.Json.Required.Always)]
        [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
        public string OrderNumber { get; set; }
    }
1 Answers

The main parameter of OpenApiRequestBody to focus on in your example is bodyType. Parameter Example can be omitted here and configured in a different way (explained later on). Instead of using JsonDocument rather use your Parameters class for the bodyType - this will then expose the characteristics of the Parameters class in the Swagger definition.

You can't use the Parameters class itself to represent a valid example and should instead create a dedicated class that inherits OpenApiExample<T> or in your case OpenApiExample<Parameters> then within this class override the Build method to construct your example instance of Parameters. You can then expose your example by decorating the Parameters class with the attribute [OpenApiExample<T>] (as you've done, but using an example class type!).

A couple of things worth noting about OpenApiRequestBody and this library more generally- the Description property of OpenApiRequestBody doesn't currently work as intended; the description text isn't rendered out in the Swagger requestBody definition. This leads on to my next point which is to note that (at the time of writing) this library - Microsoft.Azure.WebJobs.Extensions.OpenApi - is in a pre-release state (0.7.2) and accordingly does seem to contain some bugs, as I've discovered myself.

I've amended your ModifyOrder class below; this version I hope will help with answering your questions!

public static class ModifyOrder
{
    [FunctionName("ModifyOrder")]
    [OpenApiOperation(operationId: "run", tags: new[] { "Modify Order" })]
    [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
    [OpenApiRequestBody(contentType: "application/json", bodyType: typeof(Parameters), Description = "Parameters", Required = true)]
    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "text/plain", bodyType: typeof(string), Description = "The OK response")]
    public static async Task<IActionResult> run(
        [HttpTrigger(AuthorizationLevel.Function, "PUT", Route = null)] HttpRequest req,
        ILogger log)
    {
        log.LogInformation($"C# HTTP trigger function processed a request.");

        string ordernumber;

        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        dynamic data = JsonConvert.DeserializeObject(requestBody);
        ordernumber = data?.orderno;

        string responseMessage = $"Order:{ordernumber}";
        return new OkObjectResult(responseMessage);
    }
}

[OpenApiExample(typeof(ParametersExample))]
public class Parameters
{
    /// <summary>The id of the customer in the context. This is also called payer, sub_account_id.</summary>
    [OpenApiPropertyDescription("The id of the customer in the context. This is also called payer, sub_account_id.")]
    [Newtonsoft.Json.JsonProperty("customerId", Required = Newtonsoft.Json.Required.Always)]
    [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
    public string CustomerId { get; set; }

    /// <summary>The order number. Used to uniquely identify a group of order lines.</summary>
    [OpenApiPropertyDescription("The order number. Used to uniquely identify a group of order lines.")]
    [Newtonsoft.Json.JsonProperty("orderNumber", Required = Newtonsoft.Json.Required.Always)]
    [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
    public string OrderNumber { get; set; }
}

public class ParametersExample : OpenApiExample<Parameters>
{
    public override IOpenApiExample<Parameters> Build(NamingStrategy namingStrategy = null)
    {
        this.Examples.Add(
            OpenApiExampleResolver.Resolve(
                "ParametersExample",
                new Parameters()
                {
                    CustomerId = "CUST12345",
                    OrderNumber = "ORD001"
                },
                namingStrategy
            ));

        return this;
    }
}
Related