'Using FromUri to bind object without parameter name prefixes

When using ASP.NET WebApi2 and Swashbuckle/Swagger, I am trying to bind an object using the FromUri attribute, something like this:

[RoutePrefix("api/v1/example")]
public class ExampleController : ApiController
{
    [HttpGet]
    [Route("{id}")]
    public ExampleModel Get(string id, [FromUri]ExampleModel searchCriteria)
    {
    ...
    }
}

public class ExampleModel
{
    public string id { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }
}

I find that the URL to perform this GET operation and search by last name ends up being something like:

http://localhost:52259/api/v1/example/1?searchCriteria.firstName=paul

How can I remove the "searchCriteria" prefix from the query parameter? I want to keep it as an ExampleModel object in the controller's method signature, but be able to use (and have this reflected in the Swagger UI docs):

http://localhost:52259/api/v1/example/1?firstName=paul


Solution 1:[1]

There's a Name property on the FromUri attribute. If I set that to an empty string, it makes the Swagger UI docs appear without the "searchCriteria." prefix:

public ExampleModel Get(string id, [FromUri(Name = "")]ExampleModel searchCriteria)
{
...
}

It appears that the controller receives the firstName regardless of whether or not there is a "searchCriteria." prefix as part of the query parameter name.

Solution 2:[2]

I managed to solve it, I inserted a static method, follow the code below if anyone needs it

public static string ToQueryString(this object request, string separator = ",")
        {
            if (request == null)
                throw new ArgumentNullException("request");

            // Get all properties on the object
            var properties = request.GetType().GetProperties()
                .Where(x => x.CanRead)
                .Where(x => x.GetValue(request, null) != null)
                .ToDictionary(x => x.Name, x => x.GetValue(request, null));

            // Get names for all IEnumerable properties (excl. string)
            var propertyNames = properties
                .Where(x => !(x.Value is string) && x.Value is IEnumerable)
                .Select(x => x.Key)
                .ToList();

            // Concat all IEnumerable properties into a comma separated string
            foreach (var key in propertyNames)
            {
                var valueType = properties[key].GetType();
                var valueElemType = valueType.IsGenericType
                                        ? valueType.GetGenericArguments()[0]
                                        : valueType.GetElementType();
                if (valueElemType.IsPrimitive || valueElemType == typeof(string))
                {
                    var enumerable = properties[key] as IEnumerable;
                    properties[key] = string.Join(separator, enumerable.Cast<object>());
                }
            }

            // Concat all key/value pairs into a string separated by ampersand
            return string.Join("&", properties
                .Select(x => string.Concat(
                    Uri.EscapeDataString(x.Key), "=",
                    Uri.EscapeDataString(x.Value.ToString()))));
        }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Paul K.
Solution 2 Rafael Parenza