Bit of Technology

  • Archive
  • About Me
    • Advertise
    • Disclaimer
  • Speaking
  • Contact

Building ASP.Net Web API RESTful Service – Part 5

November 25, 2013 By Taiseer Joudeh 25 Comments

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:

  • ASP.NET Web API 2 Attribute Routing.
  • IHttpActionResult as new response type and CORS Support.

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:

C#
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:

FiddlerPostRequest-Courses

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:

FiddlerPostResponse-Courses

Update existing Course using HTTP PUT action

We’ll add new method named Put(int Id, CourseModel courseModel) to “CoursesController”, as the implementation below:

C#
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:

FiddlerPutRequest-Courses

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:

C#
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:

FiddlerDeleteRequest-Courses

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.

C#
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.

Source code is available on GitHub.

Related Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Identity 2.1 Accounts Confirmation, and Password Policy Configuration – Part 2
  • ASP.NET Identity 2.1 with ASP.NET Web API 2.2 (Accounts Management) – Part 1

Filed Under: ASP.NET, ASP.Net Web API, Entity Framework, Web API Tutorial Tagged With: API, ASP.NET, Entity Framework, HTTP Verbs, REST, RESTful, Tutorial, Web API, Web Service

Comments

  1. zevs7772006 says

    January 4, 2014 at 2:30 am

    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 *

    Reply
  2. Bart Calixto says

    January 10, 2014 at 12:38 am

    Delete action when object not found should not return not found. should return ok.

    Reply
    • tjoudeh says

      January 10, 2014 at 1:54 am

      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.

      Reply
      • pmarfleet says

        February 1, 2014 at 6:03 pm

        204 (No Content) would be the most appropriate status code to return when a resource has been successfully deleted.

        Reply
        • tjoudeh says

          February 1, 2014 at 6:14 pm

          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.

          Reply
  3. Deepak Singh says

    March 12, 2014 at 2:14 pm

    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..

    Reply
    • Taiseer Joudeh says

      March 12, 2014 at 2:20 pm

      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?

      Reply
  4. Lenny says

    April 23, 2014 at 9:44 pm

    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?

    Reply
    • Taiseer Joudeh says

      April 24, 2014 at 2:10 pm

      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.

      Reply
  5. Mohamed Sadek says

    April 29, 2014 at 12:37 pm

    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;
    }
    }
    }
    }

    Reply
    • Taiseer Joudeh says

      April 29, 2014 at 4:37 pm

      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.

      Reply
  6. Mohamed Sadek says

    April 29, 2014 at 7:35 pm

    Thanks,

    Au maximum 2 hours and I will give you feedback.
    I feel that I’m too close to the solution.

    Lots of regards,

    Reply
  7. Mohamed Sadek says

    April 29, 2014 at 9:46 pm

    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,

    Reply
    • Taiseer Joudeh says

      April 29, 2014 at 10:39 pm

      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 🙂

      Reply
  8. Mohamed Sadek says

    April 30, 2014 at 11:41 am

    I will use it,
    Thank you for the precious advice,

    Reply
  9. Gary Ray says

    August 1, 2014 at 12:14 am

    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?

    Reply
    • Taiseer Joudeh says

      August 1, 2014 at 1:10 am

      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.

      Reply
  10. Rob says

    August 4, 2014 at 9:41 pm

    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 !

    Reply
    • Taiseer Joudeh says

      August 5, 2014 at 11:40 am

      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?

      Reply
  11. Amjad Alhusseini says

    December 18, 2014 at 10:44 am

    Small correction, there is a missing return in line 10 on the PUT/PATCH code snippet above, before Request.CreateErrorResponse.

    Reply
    • Taiseer Joudeh says

      December 20, 2014 at 6:47 pm

      Thanks Amjad for the heads up, you can issue Pull request.

      Reply
  12. Serge says

    May 19, 2015 at 6:11 pm

    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!

    Reply
  13. Thunder says

    August 25, 2016 at 10:12 am

    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?

    Reply
    • Taiseer Joudeh says

      August 25, 2016 at 3:37 pm

      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.

      Reply
      • Thunder says

        August 25, 2016 at 11:43 pm

        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.

        Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

About Taiseer

Husband, Father, Consultant @ MSFT, Life Time Learner... Read More…

Buy me a coffeeBuy me a coffee

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Blog Archives

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Tags

AJAX AngularJS API API Versioning ASP.NET ASP.Net 5 Authentication Autherization Server Azure Active Directory Azure Active Directory B2C Azure AD B2C basic authentication Code First Dependency Injection Documentation Entity Framework Entity Framework 7 Facebook Foursquare API Google Authenticator Identity jQuery JSON JSON Web Tokens JWT MVC 6 Ninject OAuth OData Resource Server REST RESTful RESTful. Web API Single Page Applications SPA Swagger-ui Swashbuckle TFA Token Authentication Tutorial Two Factor Authentication Web API Web API 2 Web API Security Web Service

Search

Copyright © 2022 · eleven40 Pro Theme on Genesis Framework · WordPress · Log in