Reputation: 625
Is it possible to have a route like the following:
MyApi/v1/Events // gets all the events from the db
MyApi/v1/Events?Longitude=4.4777&Latitude=51.9244 //get request for a specific location
In my code, I use attribute based routing with a route prefix:
[Route("")]
// GET: MyApi/v1/Events
public IEnumerable<EventDto> Get()
{
return EventService.GetAllEvents().ToDto();
}
[Route("{Latitude}/{Longitude}/{Radius}")]
// GET: MyApi/v1/Events?Latitude={doubleVal}&Longitude={doubleVal}
public IEnumerable<EventDto> Get(double? Latitude, double? Longitude, double? Radius)
{
return EventService.ReadEvent(Latitude.Value, Longitude.Value, Radius.Value).ToDto();
}
Also, I use a route prefix on the controller:
[RoutePrefix(WebApiConfig.ClientAppName + "/v1/Events")]
It seems only the Get all and not the parameter version of my api is triggered. I tried different variants of the Route attribute with no success. In my WebApiConfig:
config.Routes.MapHttpRoute(
name: MTRouteName,
routeTemplate: ClientAppName + "/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Upvotes: 1
Views: 973
Reputation: 3329
Obviously you want to explore the opportunities of Attribute Routing in ASP.NET Web API 2 to support certain URI patterns that are common in RESTful APIs.
Your specific use case to enable API clients to query the event resource collection based on latitude, longitude and radius is even supported by what is nowadays called convention-based routing.
https://{base_uri}/MyApi/v1/Events?Longitude=4.4777&Latitude=51.9244
public EventsController : ApiController
{
public IEnumerable<EventDto> GetEvents([FromUri] double? latitude, [FromUri] double? longitude)
{
//code validating method parameters
return EventService.ReadEvent(Latitude.Value, Longitude.Value).ToDto()
}
}
Using [FromUri] attributes on action parameters is called "Parameter binding". Parameter binding can be useful if you want to force ASP.NET Web API to read a complex type from the URI. Your application is about Events and geospatial concerns and encapsulating these concerns into complex types as you already did with events helps documenting any interface constraints. What would be the meaning of a query for events when only a value for longitude is provided? Given that omitting nullable parameters helps in your specific use case.
The following example defines a GeoPoint type, along with a controller method that gets the GeoPoint from the URI.
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public EventsController : ApiController
{
//action is only selected when URI contains required parameters to bind a GeoPoint instance
public IEnumerable<EventDto> GetEvents([FromUri] GeoPoint geoPoint)
{
//how about passing a complex type to the EventService?
return EventService.ReadEvent(geoPoint).ToDto()
}
}
A URI returning a collection of events within a given distance/radius could be like
https://{base_uri}/MyApi/v1/Events?Longitude=4.4777&Latitude=51.9244&Radius=10.000
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public class GeoPointQuery
{
public double Latitude { get; set; }
public double Longitude { get; set; }
public double Radius {get; set; }
}
public EventsController : ApiController
{
public IEnumerable<EventDto> GetEvents([FromUri] GeoPoint geoPoint)
{
//how about passing a complex type to the EventService?
return EventService.GetEventAtPoint(geoPoint).ToDto()
}
public IEnumerable<EventDto> GetEventsQuery([FromUri] GeoPointQuery geoPointQuery)
{
//how about passing a complex type to the EventService?
return EventService.GetEventsWithinDistance(geoPointQuery).ToDto()
}
}
Applying Attribute routing will help you addressing more hierarchical URI patterns and has a powerful language to define route constraints.
Upvotes: 1
Reputation: 16498
Your parameterized method expects the route MyApi/v1/Events/{Latitude}/{Longitude}/{radius}
(i.e. the parameter values are in the path itself) but your request using the query string parameters maps to route MyApi/v1/Events
.
Either format the request to include the parameters in the path or include the parameters in your default Get()
action method with null
default values and determine which EventService
method to call based on the presence of values for those parameters.
Upvotes: 1