This is the sixth part of Building ASP.Net Web API RESTful Service Series. The topics we’ll cover are:
- Building the Database Model using Entity Framework Code First – Part 1.
- Applying the Repository Pattern for the Data Access Layer – Part 2.
- Getting started with ASP.Net Web API – Part 3.
- Implement Model Factory, Dependency Injection and Configuring Formatters – Part 4.
- Implement HTTP actions POST, PUT, and DELETE In Web API – Part 5.
- Implement Resources Association – Part 6 (This Post).
- Implement Resources Pagination – Part 7.
- Securing Web API – Part 8.
- Preparing Web API for Versioning – Part 9.
- Different techniques to Implement Versioning – Part 10.
- Caching resources using CacheCow and ETag – Part 11.
Update (2014-March-5) Two new posts which cover ASP.Net Web API 2 new features:
Implement Resources Association
In this post we’ll be covering the relationship between resources which is called “Resources Association”, for example there is a relation between each “Course” and “Students” i.e. each course has multiple students enrolled in it. So we can build an absolute URI in the form:”api/courses/courseid/students/{userName}”, so if we want to list all students enrolled in course id 5 our GET request will be as: “api/courses/5/students/”. If we want to enroll student his username is “TaiseerJoudeh” in course id 5 we’ll issue POST request to the URI: ”api/courses/5/students/TaiseerJoudeh” and so on.
To implement this we need add new route in “WebApiConfig” class named “Enrollments” as the code below:
1 2 3 4 5 |
config.Routes.MapHttpRoute( name: "Enrollments", routeTemplate: "api/courses/{courseId}/students/{userName}", defaults: new { controller = "Enrollments", userName = RouteParameter.Optional } ); |
Notice how is “courseId” parameter is not optional and “userName” parameter is optional.
Now we’ve to add new controller named “EnrollmentsController”, we’ll support two HTTP verbs, GET to list all students in specific class, and POST to enroll student in specific calls, you’ll notice that we’ve implemented pagination for GET method, we’ll cover this topic in the next post. The controller code listed as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public class EnrollmentsController : BaseApiController { public EnrollmentsController(ILearningRepository repo) : base(repo) { } public IEnumerable<StudentBaseMode> Get(int courseId, int page = 0, int pageSize = 10) { IQueryable<Student> query; query = TheRepository.GetEnrolledStudentsInCourse(courseId).OrderBy(s => s.LastName); var totalCount = query.Count(); System.Web.HttpContext.Current.Response.Headers.Add("X-InlineCount", totalCount.ToString()); var results = query .Skip(pageSize * page) .Take(pageSize) .ToList() .Select(s => TheModelFactory.CreateSummary(s)); return results; } public HttpResponseMessage Post(int courseId, [FromUri]string userName, [FromBody]Enrollment enrollment) { try { if (!TheRepository.CourseExists(courseId)) return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not find Course"); var student = TheRepository.GetStudent(userName); if (student == null) return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not find Student"); var result = TheRepository.EnrollStudentInCourse(student.Id, courseId, enrollment); if (result == 1) { return Request.CreateResponse(HttpStatusCode.Created); } else if (result == 2) { return Request.CreateResponse(HttpStatusCode.NotModified, "Student already enrolled in this course"); } return Request.CreateResponse(HttpStatusCode.BadRequest); } catch (Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex); } } } |
In the POST method notice how we’re using FromUri and FromBody attribute to indicate from where we’re getting the values of the request, we’ll get the “userName” from the URI and the “enrollment” object from Request Body. The “enrollment” object will contain the enrollment data only, we could send the “CourseID” and “StudentID” as well, but it makes more sense to get both values from the URI.
To test the POST request we’ll use fiddler to issue a POST request, we want to enroll student “TaiseerJoudeh” in course with ID=5, the request will be as the image below:
As we’ve learned before, we will return HTTP status code as a result for this POST operation, if the request is successful we will return 201 (Resource Created), if the student already enrolled in this class we’ll return status code 304 (Not modified).
In the next post we’ll talk about pagination for large results, using manual pagination, and how we return pagination meta-data.
How would you architect your code so that you can also ask for students directly without going trougth the course property? like /api/student/studentName ? wouldn’t that make the resource have 2 locations ? how is this implemented?
Hi, am really sorry for the late reply, there is already route for api/students/{userName} where you can query single student. And thats fine if the same resource have 2 end points while you are doing associations. Hope this answers your question.
this Tutorial Is one of the best I ever saw !!!!! you help me allot Thank you !!
Really glad to hear this, good luck in
working with Web API. And keep tuned for another tutorial covering different areas of Web API.
I agreed that it has given me a great start… will convert those to a videos forms to demonstrate at my end.
This tutorial is one of the best available until now. Please share more information on web APIs
Glad it is useful Harleen, thanks for your comment!
Hey Taiser, i notice that when you post a request to join the group or create association, i am seeing that it says 401 Unauthorized error. can you show an example on how to get the authorization and post it or how to get over this?
Hi Randi, this has been for a while 🙁 sorry for not being able to troubleshoot this one 🙁
Hello Dear,
I am getting the issue “This operation requires IIS integrated pipeline mode.” while using this URL “http://localhost:50688/api/courses/5/students” VS-2012
Hey Taiser, During the POST process, “400 Bad Request” is always returned. “result” returns 0. Why is that?