I have the following rest schema that I'd like to implement using the ASP.NET Web Api:
http://mydomain/api/students
http://mydomain/api/students/s123
http://mydomain/api/students/s123/classes
http://mydomain/api/students/s123/classes/c456
I've got the first two links working properly using the ApiController and the following two methods:
public class StudentsController : ApiController {
// GET api/students
public IEnumerable<Student> GetStudents() {
}
// GET api/students/5
public IEnumerable<Student> GetStudent(string id) {
}
}
In this same controller, (or do I need a different controller called ClassesController?), how would I implement the last two links? Also, what would the routing for the 'classes' part look like (if necessary)?
Here's my WebApiConfig (which I'd like to keep as dynamic, rather than hard-coding the route to the /classes if possible:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// EDIT - I'm getting 404's when trying to use this
context.Routes.MapHttpRoute(
name: "JobsApi",
routeTemplate: this.AreaName + "/Students/{id}/Classes/{classId}",
defaults: new { classId = RouteParameter.Optional }
);
EDIT Here's my newly created ClassesController:
public class ClassesController : ApiController {
// GET api/classes
public IEnumerable<TheClass> Get(string id) {
return null;
}
}
I'm getting 404 Errors when attempting to go to this URL:
http://mydomain/api/students/s123/classes
Routing in ASP.NET can express these more complex rules but needed to be explicitly set up. For example in this case you would have to define 2 routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/students/{studentId}/{controller}/{classId}",
defaults: new { classId = RouteParameter.Optional }
);
And you would have a controller for it:
public class ClassesController
{
public TheClass Get(int studentId, int classId)
{
....
}
}
This is perhaps not ideal but the main option.
I was working on a hierarchical routing which was not possible due to an implementation issue in Web API but this issue has been fixed now so I might start working on it again.
With this nice hierarchical approach, you have more concerns that routing internally. There is a good sample application which adopts the hierarchical resource structure: PingYourPackage. Check that out.
Note: I have a blog post about this issue which explains the below concerns and gives solutions to those with a few code samples. You can check it out for more details:
- Hierarchical Resource Structure in ASP.NET Web API
Let me explain the concerns here briefly by setting up a sample scenario. This may not be the desired approach for these type of situations but lays out the concerns very well. Let's say you have the below two affiliates inside your database for a shipment company:
And then assume that these affiliates has some shipments attached to them:
Finally, we want to have the following resource structure:
GET api/affiliates/{key}/shipments
GET api/affiliates/{key}/shipments/{shipmentKey}
POST api/affiliates/{key}/shipments
PUT api/affiliates/{key}/shipments/{shipmentKey}
DELETE api/affiliates/{key}/shipments/{shipmentKey}
@Ali already explained it but I've a different approach here. Assume that we are sending a GET request against /api/affiliates/105/shipments/102
. Notice that the affiliate key is 105 here which doesn't exist. So, we would want to terminate the request here ASAP. We can achieve this with a per-route message handler.
If you have some type of authentication in place, you would want to make sure (in our scenario here) that the authenticated user and the requested affiliate resource is related. For example, assume that Affiliate1 is authenticated under the Affiliate role and you have the AuthorizeAttribute
registered to check the "Affiliate" role authorization. In this case, you will fail miserably because this means that Affiliate1 can get to the following resource: /api/affiliates/101/shipments
which belongs to Affiliate2. We can eliminate this problem with a custom AuthorizeAttribute
.
Now, the following URI should get me the correct data:
GET /api/affiliates/100/shipments/102
However, what would happen for the below URI:
GET /api/affiliates/100/shipments/103
This should get you "404 Not Found" HTTP response because affiliate whose Id is 100 doesn't own the shipment whose id is 103.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With