This is the fifth 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 (This Post).
- Implement Resources Association – Part 6.
- 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 HTTP actions POST, PUT, and DELETE In Web API
In this post we’ll continue implementing the other HTTP actions for “CoursesController”, then discuss briefly what we’ll implement in “StudentsController”.
Create new Course using HTTP Post action
We’ll add new method named Post(CourseModel courseModel) to “CoursesController” as the implementation below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public HttpResponseMessage Post([FromBody] CourseModel courseModel) { try { var entity = TheModelFactory.Parse(courseModel); if (entity == null) Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not read subject/tutor from body"); if (TheRepository.Insert(entity) && TheRepository.SaveAll()) { return Request.CreateResponse(HttpStatusCode.Created, TheModelFactory.Create(entity)); } else { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not save to the database."); } } catch (Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex); } } |
In the above implementation we’ve to note four things:
- Method named is “Post”, so the clients needs to issue HTTP Post request.
- The method accepts parameter of type CourseModel, in Web API parameters with complex types are deserialized from the request body. So the client has to send a serialized representation of a CourseModel object in JSON format.
- We’ve returned proper “HttpResponseMessange” for all possible scenarios that might happen when we execute this operation. In case the resource is created successfully, server should return HTTP response 201(Resource Created) along with the resource created. It is very important to return the resource created in response message because it will contain CourseId generated at the server.
- We’ve introduced method named “Parse” in “ModelFactory” class which is responsible to parse the “CourseModel” back to the “Course” domain model object which can be added to our repository. You can use GitHub to browse the latest version of “ModelFactory” and “BaseApiController” classes in order to check the changes introduced to apply “Parse” function.
To test this we need to open fiddler and choose the Composer tab, we’ll issue a POST request to the URI: http://localhost:{your_port}/api/courses/ The request will look as the image below:
In this HTTP POST request we need to note the following:
- The “content-type” header is set to “application/json” because we are sending JSON data in request body.
- The “accept” header is set to “application/json” because we are returning response in JSON format.
- The request body contains deserialized JSON data of the complex “CourseModel”, as we mentioned before, each course has a tutor and related to subject, so sending the Ids for subject and tutor is enough because the “TheModelFactory.Parse” function is responsible to retrieve those objects from database and build valid domain object model which can be used with our repository.
If this POST request executed successfully at the server, and the a new course is created we’ll receive status code 201 (Resource Created) in response header along with the new course created on the server in response body. You can check the image blow:
Update existing Course using HTTP PUT action
We’ll add new method named Put(int Id, CourseModel courseModel) to “CoursesController”, as the implementation 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 |
[HttpPatch] [HttpPut] public HttpResponseMessage Put(int id, [FromBody] CourseModel courseModel) { try { var updatedCourse = TheModelFactory.Parse(courseModel); if (updatedCourse == null) Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Could not read subject/tutor from body"); var originalCourse = TheRepository.GetCourse(id, false); if (originalCourse == null || originalCourse.Id != id) { return Request.CreateResponse(HttpStatusCode.NotModified, "Course is not found"); } else { updatedCourse.Id = id; } if (TheRepository.Update(originalCourse, updatedCourse) && TheRepository.SaveAll()) { return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(updatedCourse)); } else { return Request.CreateResponse(HttpStatusCode.NotModified); } } catch (Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex); } } |
In the above implementation we’ve to note the below:
- Method named “PUT”, so the clients needs to issue HTTP PUT request, but we’ve added “HttpPatch” attribute to the Put method, so client can issue PUT or PATCH request and both will be executed using Put method. The difference between Put and Patch that if we want to update all fields of the “CourseModel” we need to use PUT, if we want to update partial fields we need to use PATCH, in our implementation we do not need to distinguish between those two actions.
- Put method accepts two parameters, the Id of the updated resource which is set in URI, and the updated “CourseModel” which represents complex type deserialized in the request body.
- We’ve returned proper “HttpResponseMessange” for all possible scenarios that might happen when we execute this operation. In case the resource is updated successfully, server should return HTTP response 200 (OK) along with the resource created. If the resource is not modified the server should return HTTP response 304 (Not modified).
To test this we need to use fiddler and choose the Composer tab, we’ll issue a PUT request to the URI: http://localhost:{your_port}/api/courses/33 The request will look as the image below:
In this HTTP PUT request we need to note the following:
- The “content-type” header is set to “application/json” because we are sending JSON data in request body.
- The “accept” header is set to “application/json” because we are returning response in JSON format.
- The request body contains deserialized JSON data of the updated “CourseModel”.
If this PUT request executed successfully at the server, and the course is updated we’ll receive status code 200 (OK) in response header along with the updated course on the server in response body.
Delete Course using HTTP DELETE action
We’ll add new method named Delete(int Id) to “CoursesController”, as the implementation 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 |
public HttpResponseMessage Delete(int id) { try { var course = TheRepository.GetCourse(id); if (course == null) { return Request.CreateResponse(HttpStatusCode.NotFound); } if (course.Enrollments.Count > 0) { return Request.CreateResponse(HttpStatusCode.BadRequest, "Can not delete course, students has enrollments in course."); } if (TheRepository.DeleteCourse(id) && TheRepository.SaveAll()) { return Request.CreateResponse(HttpStatusCode.OK); } else { return Request.CreateResponse(HttpStatusCode.BadRequest); } } catch (Exception ex) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message); } } |
In the above implementation we’ve to note the below:
- Method named “DELETE”, so the clients needs to issue HTTP DELETE request.
- Delete method accepts the Id parameter of the deleted course, Id is set in URI, and the request body is empty.
- We’ve returned proper “HttpResponseMessange” for all possible scenarios that might happen when we execute this operation. In case the resource is deleted successfully, server should return HTTP response 200 (OK). If the deletion of the resource failed server should return HTTP response 400 (Bad request) with explanation content why the request failed to process.
To test this we need to use fiddler and choose the Composer tab, we’ll issue a DELETE request to the URI: http://localhost:{your_port}/api/courses/33 Notice that the body of the request is empty, the request will look as the image below:
Adding Students Controller to the Project
The new controller “StudentsController” will be responsible to do CRUD operations on Students. The controller will be responsible for executing the actions below:
- List all available students by sending GET request to URI: http://localhost:{your_port}/api/students/
- List single student by sending GET request to URI: http://{your_port}/api/students/HasanAhmad Note that we are identifying resource by passing userName not Id here. This method is secured using basic authentication, so resource owner only can query his detailed information once he provide userName and password. We’ll cover this in details once we talk about Securing Web API.
- Add new student by sending POST request to URI: http://localhost:{your_port}/api/students/
- Update existing student by sending PUT/PATCH request to URI: http://localhost:{your_port}/api/students/{userName}
- Delete student by sending DELETE request to URI: http://localhost:{your_port}/api/students/{userName}
I won’t list the code for “StudentsController” here as it is some how identical to “CoursesController”, you can browse it on GitHub. I will just list the new route configuration we’ve added to the “WebApiConfig” class.
1 2 3 4 5 |
config.Routes.MapHttpRoute( name: "Students", routeTemplate: "api/students/{userName}", defaults: new { controller = "students", userName = RouteParameter.Optional } ); |
So in the next post we’ll cover how to implement Resources Association.
In the Put method you have
(*)var originalCourse = TheRepository.GetCourse(id);
Then you check if originalCourse.Id != id
But how originalCourse.Id can not be equal to id after *
Delete action when object not found should not return not found. should return ok.
As long as the resource not found on the server then we can return 404 because there is nothing removed from the server. 200 Ok means that deletion took place successfully which is not the case.
204 (No Content) would be the most appropriate status code to return when a resource has been successfully deleted.
I agree with you, returning 204 is the most appropriate after deleting a resource, but I have used another APIs which they return 200 after delete. Will update this soon and thanks for your input.
Hello Taiseer, First of all thank you for excellent tutorial on WebAPI. I was getting error message “This operation requires IIS integrated pipeline mode” in StudentController. Could you plz replace with correct code or explain why am i getting the error if i am wrong
Incorrect
System.Web.HttpContext.Current.Response.Headers.Add(“X-Pagination”, Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));
Correct
I have change to System.Web.HttpContext.Current.Response.AddHeader(“X-Pagination”,
Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader));.
Once again excellent blog..
Hello Deepak, glad you enjoyed the series, may I know what VS and .net framework version you are using? At my end it’s working correctly on VS2012 and .net 4.0.
Regarding the integrated pipeline issue, do you use IIS express or local Dev server to host the web API?
Dear Taiseer,
What an excellent presentation. I’ve been researching this subject for a month now and have been compiling resources. To date yours is the most up-to-date and concise with the perfect amount of depth without being too verbose. I do have a question. Can your model factory be replaced with some sort of mapping framework like autoMapper or the like?
Hello Lenny,
Glad you like this tutorial, and thanks for your compliment.
Absolutely you can use Auto Mapper instead of model factory pattern, I just prefer this one because it gives you the ability to manipulate your response before returning it.
Hello Taisser,
Finally, I’m reaching your fifth part of Building ASP.Net Web API RESTful Service Series. I had to thank you more and more for your perfection.
But, unfortunately,
” A first chance exception of type ‘System.NullReferenceException’ occurred in portable.Web.dll
mobile :A first chance exception of type ‘System.ArgumentNullException’ occurred in EntityFramework.dll”, messages in my Immediate window during the debugging annoyed me for a long time when I’m trying to add item in my DB ( using Post ( ) ).
Today, I said: enough! and I decide to write you for advice because I don’t have much time to complete my school project.
i.e: When I test Get( ) and GetMobile( int id), it work without any exception and return me all informations what I need from my DB.
Thank you for everything,
Lots regard,
using portable.Data;
using portable.Data.Models;
using portable.Web.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace portable.Web.Controllers
{
public class MobilesController : BaseApiController
{
public MobilesController(IPortableDBRepository repo)
: base(repo)
{
}
public IEnumerable Get()
{
IQueryable query;
query = TheRepository.GetAllMobiles();
var results = query
.ToList()
.Select(s => TheModelFactory.Create(s));
return results;
}
public HttpResponseMessage GetMobile(int id)
{
try
{
var mobile = TheRepository.GetMobile(id);
if (mobile != null)
{
return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(mobile));
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
public HttpResponseMessage Post([FromBody] MobileModel courseModel)
{
try
{
var entity = TheModelFactory.Parse(courseModel);
// Debug.Write(“mobile :” + entity);
if (entity == null) Request.CreateErrorResponse(HttpStatusCode.BadRequest, “Could not read subject/tutor from body”);
if (TheRepository.Insert(entity) && TheRepository.SaveAll())
{
return Request.CreateResponse(HttpStatusCode.Created, TheModelFactory.Create(entity));
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, “Could not save to the database.”);
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
}
}
using portable.Data;
using portable.Data.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http.Routing;
namespace portable.Web.Models
{
public class ModelFactory
{
private UrlHelper _UrlHelper;
private IPortableDBRepository _repo;
public ModelFactory()
{
}
public ModelFactory(HttpRequestMessage request, IPortableDBRepository repo)
{
_UrlHelper = new UrlHelper(request);
_repo = repo;
}
public MobileModel Create(Mobile mobile)
{
return new MobileModel()
{
Url = _UrlHelper.Link(“Mobiles”, new { id = mobile.MobileId }),
MobileId = mobile.MobileId,
nom = mobile.nom,
quantite = mobile.quantite,
};
}
public Mobile Parse(MobileModel model)
{
try
{
var course = new Mobile()
{
nom = model.nom,
quantite = model.quantite,
};
return course;
}
catch (Exception)
{
return null;
}
}
}
}
Hello Mohamed,
There is nothing wrong on your code, you were able to save the item in your database successfully or you are facing an exception?
The “First chance exception” is about debugger configuration, you can turn it of by un-checking the setting (Options->Debugging->General->Redirect all Output Window text to the Immediate Window in Tools).
Please read this post on stackoverflow to understand what is the first chance exception, this is something not related to Asp.Net Web API.
Hope this will help.
Thanks,
Au maximum 2 hours and I will give you feedback.
I feel that I’m too close to the solution.
Lots of regards,
I started from Your advice ” There is nothing wrong on your code” and finally I found the problem.
In fact, I missed comma when I sent my request. ( I’m using advanced Rest client and I thought that it can understand without comma )
My request was like this :
{
nom: test
qauntite: 24
}
but I had to add comma to be like this :
{
nom: test,
qauntite: 24
}
My this can help somenone in the futur,
Thank you Taisser,
You are welcome, I advice you to use Postman to issue your http requests and if you have complex JSON object always use JSON editor such as JSON editor online to validate your request payload. Good luck 🙂
I will use it,
Thank you for the precious advice,
Thanks for a fantastic tutorial – Much better details and closer to real life than most of the others I have seen. One question on this step though:
In the Post and Put methods you have a guard condition checking whether the parsed object is null:
if (entity == null)
Request.CreateErrorResponse(…);
Seems to me like it should be:
if (entity == null)
return Request.CreateErrorResponse(…);
Otherwise you will continue to process the rest of the function event though the entity object (or updatedCourse object in the case of the POST) is null, and you will never return the appropriate message with the BadRequest since what will actually be returned is the BadRequest created in the catch{}.
Am I missing something?
Hi Gary,
Glad that you find this tutorial useful.
Well you are right I should used “return” to return the correct error response, I’ll update the source code soon, thanks for your feedback.
Great tutorial ! I am new to Web API. I thought it was supposed to be rather easy to flip back and forth between sending / receiving XML and JSON. I tried making a coupe changes in the program (as well as fiddler), but ran into errors. If it is an easy thing to do, can you provide any guidance ? Many thanks again for sharing this !
Hi Rob, Glad you liked it.
Web Api supports JSON and XML response by default, you need to set the preferred content type in Accept header.
As far as I remember i removed the XML response explicitly from my API, I need to support JSON only.
What is the error you faced?
Small correction, there is a missing return in line 10 on the PUT/PATCH code snippet above, before Request.CreateErrorResponse.
Thanks Amjad for the heads up, you can issue Pull request.
Hi, Taiseer,
Thank for tutorial.
I take StudentsController from GitHub but get a compile error at this line:
var urlHelper = new UrlHelper(Request);
“Argument type “System.Net.Http.HttpRequestMessage” is not assignable to parameter type “System.Web.Routing.RequestContext”. What did I wrong? Thanks!
I would like to ask you a question which is about PUT method. Our model already has an id property. So why we need id parameter in PUT method ? Is there any special reason for this?
Hi Thunder, well it is a REST design specification to include the ID of the updated resource in your request, but if you already have models that contain the ID, then you can update the resource without sending the Id in the URL.
Well, Which one is the best for developing REST?
Also, do you have any books that you recommend to read and learn REST design specifications?
By the way, thanks for these articles that certainly broadened my horizons.