Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swashbuckle ASP.NET Core consume application/x-www-form-urlencoded

I have an Action that consumes application/x-www-form-urlencoded:

[HttpPost("~/connect/token"), Consumes("application/x-www-form-urlencoded")]
public async Task<IActionResult> Exchange([FromBody]OpenIdConnectRequest request)
{
   ..
}

But Swashbuckle generates empty array for Consumes property. If I change it to application/json, consumes array is generated properly.

Is it a bug related to application/x-www-form-urlencoded or I need to configure Swashbuckle additionally to support this application type?

like image 232
SiberianGuy Avatar asked Nov 08 '22 02:11

SiberianGuy


1 Answers

That 'consumes' is not catered for out-of-the-box for Swashbuckle, a custom extension is required like the ones in this part of @domaindrivendev's GitHub project

There are three steps:

  1. Create Parameter Attribute
  2. Create Extension
  3. Add instruction to Swashbuckle to process the new extension
  4. Add an attribute to the parameter in an Controller method

I'll add more instructions in my fork of the repo, but here is the code:

1. FromFormDataBodyAttribute.cs

using System;
using System.Collections.Generic;
using System.Net.Http.Formatting;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Validation;

/// <summary>
/// FromFormDataBody Attribute
/// This attribute is used on action parameters to indicate
/// they come only from the content body of the incoming HttpRequestMessage.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
public sealed class FromFormDataBodyAttribute : ParameterBindingAttribute
{
    /// <summary>
    /// GetBinding
    /// </summary>
    /// <param name="parameter">HttpParameterDescriptor</param>
    /// <returns>HttpParameterBinding</returns>
    public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
    {
        if (parameter == null)
            throw new ArgumentNullException("parameter");

        IEnumerable<MediaTypeFormatter> formatters = parameter.Configuration.Formatters;
        IBodyModelValidator validator = parameter.Configuration.Services.GetBodyModelValidator();

        return parameter.BindWithFormatter(formatters, validator);
    }
}

2 AddUrlFormDataParams.cs

using Swashbuckle.Swagger;
using System.Linq;
using System.Web.Http.Description;

/// <summary>
/// Add UrlEncoded form data support for Controller Actions that have FromFormDataBody attribute in a parameter
/// usage: c.OperationFilter<AddUrlFormDataParams>();
/// </summary>
public class AddUrlFormDataParams : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var fromBodyAttributes = apiDescription.ActionDescriptor.GetParameters()
            .Where(param => param.GetCustomAttributes<FromFormDataBodyAttribute>().Any())
        .ToArray();

        if (fromBodyAttributes.Any())
            operation.consumes.Add("application/x-www-form-urlencoded");

        foreach (var headerParam in fromBodyAttributes)
        {
            if (operation.parameters != null)
            {
                // Select the capitalized parameter names
                var parameter = operation.parameters.Where(p => p.name == headerParam.ParameterName).FirstOrDefault();
                if (parameter != null)
                {
                    parameter.@in = "formData";//NB. ONLY for this 'complex' object example, as it will be passed as body JSON.
//TODO add logic to change to "query" for string/int etc. as they are passed via query string.
                }
            }
        }
    }
}

3 Update Swagger.config

 //Add UrlEncoded form data support for Controller Actions that have FromBody attribute in a parameter
c.OperationFilter<AddUrlFormDataParams>();

4. Add an attribute to the parameter in an Controller method

[FromFormDataBody]OpenIdConnectRequest request
like image 109
OzBob Avatar answered Jan 04 '23 02:01

OzBob