How to Configure Openapirequestbody in Azure Function?

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

1 Answer

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;
    }
}
4

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Chloe Bennett

Chloe Bennett

Culture, Media & Entertainment Columnist

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.

Share this article
Twitter Facebook Pinterest