Currently for every GET I have to manually create a query object from the route parameters.
Is it possible to bind directly to a query object instead?
So, instead of :
[Route("{id:int}")]
public Book Get(int id) {
var query = new GetBookByIdQuery {
Id = id
};
// execute query and return result
}
I could do this:
[Route("{id:int}")]
public Book Get(GetBookByIdQuery query) {
// execute query and return result
}
Where GetBookByIdQuery looks like:
public class GetBookByIdQuery {
public int Id { get; set;}
}
to read a complex type from the URI, [FromUri] can be used
[Route("{id:int}")]
public Book Get([FromUri] GetBookByIdQuery query) {
// execute query and return result
}
if you request api/values/2 then id property of query object will be 2;
The answer is to define you own HttpParameterBinding.
Here is the example I've made.
First I've created my CustomParameterBinding
public class CustomParameterBinding : HttpParameterBinding
{
public CustomParameterBinding( HttpParameterDescriptor p ) : base( p ) { }
public override System.Threading.Tasks.Task ExecuteBindingAsync( System.Web.Http.Metadata.ModelMetadataProvider metadataProvider, HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken )
{
// Do your custom logic here
var id = int.Parse( actionContext.Request.RequestUri.Segments.Last() );
// Set transformed value
SetValue( actionContext, string.Format( "This is formatted ID value:{0}", id ) );
var tsc = new TaskCompletionSource<object>();
tsc.SetResult(null );
return tsc.Task;
}
}
The next step is to create custom attribute to decorate parameter:
public class CustomParameterBindingAttribute : ParameterBindingAttribute
{
public override HttpParameterBinding GetBinding( HttpParameterDescriptor parameter )
{
return new CustomParameterBinding( parameter );
}
}
And finally now controller looks like:
public class ValuesController : ApiController
{
// GET api/values/5
[Route( "api/values/{id}" )]
public string Get([CustomParameterBinding] string id )
{
return id;
}
}
So now when I call http://localhost:xxxx/api/values/5
I get: "This is formatted ID value:5"
You can use the class as the parameter, but as id is no longer a parameter in the definition of the method, it cannot be included in the Route.
public class LibraryController : ApiController
{
[HttpGet]
public Book Get(GetBookByIdQuery query)
{
// Process query... & return
}
}
You can call it with the link:
http://localhost:54556/api/Library?id=12