Skip to content

The search box in the website knows all the secrets—try it!

For any queries, join our Discord Channel to reach us faster.

JasperFx Logo

JasperFx provides formal support for Wolverine and other JasperFx libraries. Please check our Support Plans for more details.

Using AsParameters 3.13

WARNING

When you use [AsParameters], you can read HTTP form data or deserialize a request body as JSON, but not both at the same time and Wolverine will happily throw an exception telling you so if you try to do this.

TIP

Use Wolverine's pre-generated code to understand exactly how Wolverine is processing any model object decorated with [AsParameters]

Wolverine supports the ASP.Net Core AsParameters attribute usage for complex binding of a mixed bag of HTTP information including headers, form data elements, route arguments, the request body, IoC services to a single input model using the ASP.Net Core [AsParameters] attribute as a marker.

See the Microsoft documentation on AsParameters for more background.

Below is a sample from our test suite showing what is possible for query string and header values:

cs
public static class AsParametersEndpoints{
    [WolverinePost("/api/asparameters1")]
    public static AsParametersQuery Post([AsParameters] AsParametersQuery query)
    {
        return query;
    }
}

public class AsParametersQuery{
    [FromQuery]
    public Direction EnumFromQuery{ get; set; }
    [FromForm]
    public Direction EnumFromForm{ get; set; }

    public Direction EnumNotUsed{get;set;}

    [FromQuery]
    public string StringFromQuery { get; set; }
    [FromForm]
    public string StringFromForm { get; set; }
    public string StringNotUsed { get; set; }
    [FromQuery]
    public int IntegerFromQuery { get; set; }
    [FromForm]
    public int IntegerFromForm { get; set; }
    public int IntegerNotUsed { get; set; }
    [FromQuery]
    public float FloatFromQuery { get; set; }
    [FromForm]
    public float FloatFromForm { get; set; }
    public float FloatNotUsed { get; set; }
    [FromQuery]
    public bool BooleanFromQuery { get; set; }
    [FromForm]
    public bool BooleanFromForm { get; set; }
    public bool BooleanNotUsed { get; set; }
    
    [FromHeader(Name = "x-string")]
    public string StringHeader { get; set; }

    [FromHeader(Name = "x-number")] public int NumberHeader { get; set; } = 5;
    
    [FromHeader(Name = "x-nullable-number")]
    public int? NullableHeader { get; set; }
}

snippet source | anchor

And the corresponding test case for utilizing this:

cs
var result = await Host.Scenario(x => x
    .Post
    .FormData(new Dictionary<string, string>
    {
        { "EnumFromForm", "east" },
        { "StringFromForm", "string2" },
        { "IntegerFromForm", "2" },
        { "FloatFromForm", "2.2" },
        { "BooleanFromForm", "true" },
        { "StringNotUsed", "string3" }
    }).QueryString("EnumFromQuery", "west")
    .QueryString("StringFromQuery", "string1")
    .QueryString("IntegerFromQuery", "1")
    .QueryString("FloatFromQuery", "1.1")
    .QueryString("BooleanFromQuery", "true")
    .QueryString("IntegerNotUsed", "3")
    .ToUrl("/api/asparameters1")
);
var response = result.ReadAsJson<AsParametersQuery>();
response.EnumFromForm.ShouldBe(Direction.East);
response.StringFromForm.ShouldBe("string2");
response.IntegerFromForm.ShouldBe(2);
response.FloatFromForm.ShouldBe(2.2f);
response.BooleanFromForm.ShouldBeTrue();
response.EnumFromQuery.ShouldBe(Direction.West);
response.StringFromQuery.ShouldBe("string1");
response.IntegerFromQuery.ShouldBe(1);
response.FloatFromQuery.ShouldBe(1.1f);
response.BooleanFromQuery.ShouldBeTrue();
response.EnumNotUsed.ShouldBe(default);
response.StringNotUsed.ShouldBe(default);
response.IntegerNotUsed.ShouldBe(default);
response.FloatNotUsed.ShouldBe(default);
response.BooleanNotUsed.ShouldBe(default);

snippet source | anchor

Wolverine.HTTP is also able to support [FromServices], [FromBody], and [FromRoute] bindings as well as shown in this sample from the tests:

cs
public class AsParameterBody
{
    public string Name { get; set; }
    public Direction Direction { get; set; }
    public int Distance { get; set; }
}

public class AsParametersQuery2
{
    // We do a check inside of an HTTP endpoint that this works correctly
    [FromServices, JsonIgnore]
    public IDocumentStore Store { get; set; }
    
    [FromBody]
    public AsParameterBody Body { get; set; }
    
    [FromRoute]
    public string Id { get; set; }
    
    [FromRoute]
    public int Number { get; set; }
}

public static class AsParametersEndpoints2{
    [WolverinePost("/asp2/{id}/{number}")]
    public static AsParametersQuery2 Post([AsParameters] AsParametersQuery2 query)
    {
        // Just proving the service binding works
        query.Store.ShouldBeOfType<DocumentStore>();
        return query;
    }
}

snippet source | anchor

And lastly, you can use C# records or really just any constructor function as well and decorate parameters like so:

cs
public record AsParameterRecord(
    [FromRoute] string Id,
    [FromQuery] int Number,
    [FromHeader(Name = "x-direction")] Direction Direction,
    [FromForm(Name = "test")] bool IsTrue);

public static class AsParameterRecordEndpoint
{
    [WolverinePost("/asparameterrecord/{Id}")]
    public static AsParameterRecord Post([AsParameters] AsParameterRecord input) => input;
}

snippet source | anchor

Released under the MIT License.