This is the third part of 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 (This Post).
- 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.
- 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:
Getting started with ASP.Net Web API
Now we are ready to start building our Web API, as I stated before we can use ASP.Net Web API with MVC projects, Web Forms, or as stand alone web service. In our case we will use an empty MVC 4 template, to do this right-click on solution “eLearning” Choose Add->New Project->Web->ASP.NET MVC 4 Web Application and name your Web API “Learning.Web”->Select Empty Template. Your solution will be as the below image:
Before talking about Web API configuration and how we can build our URIs to consume the resources, we need to understand the relation between the HTTP verb and the resource we’ll consume, as example we will consider the “Course” domain model object as the resource, the below table lists the usage of HTTP verbs with “Course” resource.
Action | HTTP Verb | Relative URI |
Get all courses | GET | /api/courses |
Get single course | GET | /api/courses/id |
Add new course | POST | /api/coursesNew course is sent in POST body |
Update existing course | PUT or PATCH | /api/courses/idUpdated course is sent in POST body |
Delete course | DELETE | /api/courses/id |
Step 1: Configuring first route (Courses Route)
In order to configure this route, the MVC 4 empty template we have just used has by default a class named “WebApiConfig” inside the “App_Start” folder, this class is called at the application start event in “Global.asax” and it is responsible to configure routing for Web API, we’ll be visiting and modifying this class multiple times to configure our Web API routes.
By default there will be a route named “DefaultApi”, we need to delete this route and replace it with route below:
1 2 3 4 5 6 7 8 9 10 11 |
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "Courses", routeTemplate: "api/courses/{id}", defaults: new { controller = "courses", id = RouteParameter.Optional } ); } } |
What we’ve done here is simple, we added new route named “Courses”, and mapped this route to a URI template “api/courses/{id}”, and assigned two default values to it which they are the name of controller it will use “Courses”, and set the “id” parameter to be optional. Now the relative URI in the form /api/courses or /api/courses/5 will be routed using this route. If we didn’t specify that id is optional then the URI /api/courses won’t be valid.
Step 2: Adding First Controller (Courses Controller)
Controller in Web API is a class that handles HTTP requests from the client, now we have to add a controller which will handle all HTTP verbs issued against URI /api/courses, to do this right-click on Controllers folder->Select Add->Name the controller “CoursesController” and choose “Empty API Controller” Template.
The controller we’ve added derives from “ApiController”, It is important to name the controller as we mentioned before “CoursesController” because Web API controller selection is implemented in a way which looks for all classes derives from “ApiController” then match the first part of the class name Courses with the defaults route controller property we defined in class “WebApiConfig”.
Step 3: Action Selection inside Controllers
We’ll start by adding support for the two GET actions we defined in the table above, getting all courses, and getting single course by id.
Action selecting in Web API controllers is smart; if we created two methods Get() and GetCourse(int id), then issued a GET request to the URL /api/courses/5
the method GetCourse(int id) will be selected and executed; because by convention its starts with “Get” and the URI contains an “id” segment in the path. The same applies for other HTTP verbs (POST, PUT, DELETE). Let’s implement this as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class CoursesController : ApiController { public List<Course> Get() { ILearningRepository repository = new LearningRepository(new LearningContext()); return repository.GetAllCourses().ToList(); } public Course GetCourse(int id) { ILearningRepository repository = new LearningRepository(new LearningContext()); return repository.GetCourse(id); } } |
So if we made GET request to the URI http://localhost:{your_port}/api/courses the action Get() will be selected and executed and part of the response will be as below:
Note: I’m using JSON view plugin on Firefox to issue GET request and return data in JSON format.
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 |
[{ "Id": 1, "Name": "History Teaching Methods 1", "Duration": 5, "Description": "The course will talk in depth about: History Teaching Methods 1", "CourseTutor": { "Courses": [], "Id": 1, "Email": "Ahmad.Joudeh@outlook.com", "UserName": "AhmadJoudeh", "Password": "EFZWOFEO", "FirstName": "Ahmad", "LastName": "Joudeh", "Gender": 0 }, "CourseSubject": { "Courses": [], "Id": 1, "Name": "History" }, "Enrollments": [] }, { "Id": 2, "Name": "History Teaching Methods 2", "Duration": 4, "Description": "The course will talk in depth about: History Teaching Methods 2", "CourseTutor": { "Courses": [], "Id": 1, "Email": "Ahmad.Joudeh@outlook.com", "UserName": "AhmadJoudeh", "Password": "EFZWOFEO", "FirstName": "Ahmad", "LastName": "Joudeh", "Gender": 0 }, "CourseSubject": { "Courses": [], "Id": 1, "Name": "History" }, "Enrollments": [] }] |
Now if we try to issue another GET request to the URI http://localhost:{your_port}/api/courses/5 in order to select a single course, the action GetCourse(int id) will be selected and executed, but an exception will be thrown when trying to serialize the object graph we return from method GetCourse. the exception message will read “Self referencing loop detected for property ‘Course’ with type ‘Learning.Data.Entities.Course’. Path ‘Enrollments[0]’.”. In other words this method is trying to return chain of related objects which they reference each other (Course>Enrollment>Course>Enrollment>etc…).
So what we have implemnted in “CoursesController” till this moment is insufficient, the below points lists what are the lacking parts:
- Self referencing when returning chain of objects. This can be solved using a design pattern called the Model Factory.
- We are returning all the fields from the domain model object and leaking sensitive information to the client, for example if you take a look on “Tutor” object you will notice that we are returning the “password” field which shouldn’t be leaked to API consumer. This can be solved using the Model Factory pattern.
- Each resource returned in the response should be linked to a URI, this will simplify resources query for the client. This can be solved using the Model Factory pattern.
- We should return HTTP status code when returning single resource, i.e if the resource was not found we should return 404 in response header, if it was found we should return 200 OK, etc…, this can be solved by returning HttpResponseMessage object.
- Inside each method we are instantiating our repository, this operation is expensive as it includes opening connection to the database, we need to implement Dependency Injection pattern, this can be solved by using Ninject DI framework.
- The format for JSON response objects are in Pascal Case i.e. “FirstName”, and most probably our API will be consumed in client using JavaScript, it is easier for JS developers to work with properties formatted in Camel Case “firstName”. this can be solved by configuring JSON formatters of the response.
So in the next post we’ll cover how to fix those flaws in our Web API.
Reblogged this on Sutoprise Avenue, A SutoCom Source.
Hi,
I can see this article is very useful for WEB API. I have followed all steps from earlier post and able to create Learning.Data project. When I started with Learning.Web I am facing some issues.
As Mentioned, Get() method will return all list of courses and GetCourse(int id) will return only specific course as per parameter provided in URL. However when I pass /api/Courses/5 it is hitting back method Get() and returning all data. This is not going to GetCourse() Method.
Can you please suggest what is going wrong or anything is missing till this implementation?
Thanks.
Avinash
Hello Avinash,
Glad to hear you liked the tutorial, could you please elaborate more about your issue? It will be great to post the routes configuration in class “WebApiConfig” most probably the issue is there. As well try to fork the code in GitHub and compare it with your code as well.
I love your site so much. It has saved me a lot of time. Thanks you for your sharing.Please keep it up
You are welcome, glad you liked the posts, subscript to blog to keep updated 🙂
Hi,
I have followed steps till this part. This is very nice blog I ever seen for WEB API.
In one thing I still have issue, when I call /api/courses/5, it still hitting the Get() method and not calling the GetCourse(id) method. Even I tried to comment Get(), then I am getting 404 error.
Please do suggest.
Thank you.
Avinash
In WebAPIConfig.cs check routeTemplate: “api/yourcontrollername/{id}.
I have the same i just check it and checked that i forgot to write the controller name over here. so you may also have the same issue.
Hello,
Can you elaborate more please? Which controller you are referencing?
It’s a shame you don’t have a donate button! I’d most
certainly donate to this excellent blog! I guess for now i’ll settle for book-marking and adding your RSS feed to
my Google account. I look forward to new updates and will share this site with my
Facebook group. Talk soon!
Thanks for the compliment, sharing the knowledge with the community and receiving constructive feedback is enough for me.
Hi,
It was my bad. I was coding with my own style and created GetCourse(int CourseID) function with parameter “CourseID”, where as in Route mapping we have mentioned “{id}”. So this was not matching the URI pattern and always going to default Get() function.
I changed it accordingly and able to resolve my issue. This is good learning for me as well.
Thank you.
Avinash
Hello
Sir can you please explain the reason for –
Inside each method we are instantiating our repository, this operation is expensive as it includes opening connection to the database, we need to implement Dependency Injection pattern, this can be solved by using Ninject DI framework.
Hello Lakshay,
Please move to part 4 of this series and you will see how I’ve implemented dependency injection using Ninject.
Hi Taiseer,
I want to retrieve data from many table using repository pattern.
I’ve searched around and haven’t really found a clear answer.
This my query that I found to retreive data from many table but I can’t introduce it in my project that use repository pattern.
( I am building my school project following your nice tuto and I am almost in the end of my project that’s why I had to respect this patterrn )
This my entities query:
from prMp in Pr_Mp
join mp in MatierePremieres on prMp.MpID equals mp.MpID
join pr in Projets on prMp.PrID equals pr.PrID
where pr.PrID==1
select new {prMp.Pr_Mp_etat,mp.Mp_Designation,pr.Pr_Nom}
namespace SuiviDuChantierHost.Web.Models
{
public class MatierePremiereByProjetModel
{
// prMp.Pr_Mp_etat,mp.Mp_Designation,pr.Pr_Nom
public string Pr_Nom { get; set; }
public string Mp_Designation { get; set; }
public string Pr_Mp_etat { get; set; }
}
}
Should I have a methode create in ModelFactory. and if yes this is my attempt :
public MatierePremiereByProjetModel CreateMpByProject(Pr_Mp cmd)
{
return new MatierePremiereByProjetModel()
{
Pr_Mp_etat = cmd.Pr_Mp_etat,
Mp_Designation = cmd.MatierePremiere.Mp_Designation,
Pr_Nom=cmd.Projet.Pr_Nom
};
}
now, I don’t now how to use all this in a MpV2projectController.cs
Can you help me to fix this please,
Hi Taissir,
This is my last attempt to resolve this problem,
I had create this methode in my repository:
public IQueryable GetMatierePremierebyProjet()
{
var result = from prMp in _ctx.Pr_Mp
join mp in _ctx.MatierePremieres on prMp.MpID equals mp.MpID
join pr in _ctx.Projets on prMp.PrID equals pr.PrID
where pr.PrID == 1
select new { Pr_Mp_etat = prMp.Pr_Mp_etat, Mp_Designation = mp.Mp_Designation, Pr_Nom = pr.Pr_Nom };
return (IQueryable)result;
i.e: ResultMpPr is a new table that I created in my dataBase ( because I have a new type of object to return )
But when I run my service i got this erreur:
{“$id”:”1″,”message”:”An error has occurred.”,”exceptionMessage”:”Impossible d’effectuer un cast d’un objet de type ‘System.Data.Entity.Infrastructure.DbQuery`1[f_
So, could this attempt be a solution for my question I asked to you,
If yes how can I fix this error,
Thanks,
Hi Taissr,
Please ignore my last question.
I found solution.
This is the answer:
I created an new calss for result:
public class SomeClass
{
public string Pr_Mp_etat { get; set; }
public string Mp_Designation { get; set; }
public string Pr_Nom { get; set; }
}
public IQueryable GetMatierePremierebyProjet(int id)
{
var result = from prMp in _ctx.Pr_Mp
join mp in _ctx.MatierePremieres on prMp.MpID equals mp.MpID
join pr in _ctx.Projets on prMp.PrID equals pr.PrID
where pr.PrID == id
select new { Pr_Mp_etat = prMp.Pr_Mp_etat, Mp_Designation = mp.Mp_Designation, Pr_Nom = pr.Pr_Nom };
return result;
}
hopefully this helps someone in the future.
Hello Mohammed, sorry for not replying, it was busy weekend. Great that you found a solution and thanks for sharing the answer, Good luck in your project.
Hello Taissir,
I want to build a web service Login using WEB API 2 ( VS2012 + Web Tools 2013.1 )
For achieving my target, I search about authentification.
Is This search on the right track !?
And all tuto authentification that I found about talk about customer memberShip provider, and the possibility to create page login without writing any code !!
In my case Do I really need this Kind of thing ( customer memberShip provider)
I’m stuck in this level, I’ve never really felt held back.
What should I do to achieve my goal ?
Where should I begin to see my Web service Login come alive?
Thank you in advance for your answer,
Taiseer,
First off let me thank you for your great site. I’m new to Web Service Development and this tutorial has been fantastic.
I have a question regarding changing the data return into JSON. Right now when I call the service I’m seeing it as XML in my browser, Google Chrome. I’ve installed the JSON View extension to Google Chrome but that isn’t helping.
Thanks again and I look forward to your response.
Jon
Hi Jon,
It is all about the “Accept” header in each request you sent, so if you set it to “application/json” you will get the JSON response. Try to use tool named Fiddler or PostMan to craft the requests manually. Do not user browsers. Hope this answers your question.
Oh I see. I was seeing it in my browser but while working in Fiddler noticed I was getting JSON back. So that is something I’ll need to think about when the application we’re writing to use this service makes its calls to it.
Thank you SO much.
Regards,
Jon
Taiseer,
Could you share how you changed the output of the service from XML to JSON?
Thanks,
Jon
Hello TAISEER, I hope you’re doing fine !
I’m using this tutorial to learn about Web Api , I just finished part 3, and I have a question. When I hit http://localhost:my_port/api/courses nothing happens. Actually, I have no data to be showed because I have no idea how to use it, should I just create a json file then hit the url ?
Greetings from Tunisia
Hi, I’m doing great, thanks for adking.
Well once you issue http GET request the logic behind this endpoint should read the courses from data store or file as you duggested, complete the tutorial because I’ve implemented this fully. Good luck 🙂
Hello Taiseer,
First I would like to thank you sir. I am learning so much from you here.
Second, I’m having a problem (it must be my fault because everybody here are succeeding).
When I run the application (up to end of part 3), my chrome is opening and showing the URL:
http://localhost:46040/ + an error (HTTP 404)
I then add:
http://localhost:46040/api/users (I have users instead of courses)
and 2 things I don’t understand happen:
1. My Seed method is not being called and my database is not created
2. When the method Get() is called, repository.GetAllUsers() method throws an exception saying:
An exception of type ‘System.ArgumentException’ occurred in MyApp.Web.dll but was not handled in user code
Additional information: Object of type ‘MyApp.Data.MyAppContext’ cannot be converted to type ‘System.Data.Entity.IDatabaseInitializer`1[MyApp.Data.MyAppContext]’
Any help will be very appreciated 🙂
Thanks for you technological generosity..
Eibi
Hey how are you? i really like your blog, however, it seems that the framework doesn’t really validate what data is being post into the database. what i mean is it will allow users with the same username and password and stored it into the db. how do you get pass that?
Hi, glad that posts are useful. You are correct, this deep implementation details such as validating if the username exists before registering the user is not covered here, it didn’t add much value for the Web API covered here in the tutorial, so you can implement this for sure and do DB check before you register the user and return the right HTTP status code which is 400 in this case.
Great article. Now understand web api properly after reading your whole article series.
Always happy to help. Thanks for your comment 🙂
Hi Taiseer,
First off let me take a opportunity thank you for your great site. It has been fantastic till this page,
But after creating first controller as CoursesController and in get method while creating Ilearning repository object I am getting following error.
public List Get()
{
ILearningRepository repository = new LearningRepository(new LearningContext());
return repository.GetAllCourses().ToList();
}
The type ‘System.Data.Entity.DbContext’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’.
Hi Sudhir,
This has been for a while, I recommend you to search for such error on SO, the answer should be here
Hello Taiseer,
I followed u step by step but i always get this error when i issue this command http://localhost:19572/api/courses this method is generating the exception
public IQueryable GetAllCourses()
{
return _ctx.Courses
.Include(“CourseSubject”)
.Include(“CourseTutor”)
.AsQueryable();
}
‘System.Data.Entity.Core.ProviderIncompatibleException’ occurred in EntityFramework.dll but was not handled in user code
Additional information: An error occurred while getting provider information from the database. This can be caused by Entity Framework using an incorrect connection string. Check the inner exceptions for details and ensure that the connection string is correct.
and when i inspect the JASON element in my fiddler i always get this
ExceptionMessage=The system cannot find the file specified
ExceptionType=System.ComponentModel.Win32Exception
Message=An error has occurred.
and i am using entity framwork 6.0.1 i tried 5 but it didn’t work
Hi Taiseer Joudeh,
Sorry , it seems i got something wrong , i have followed the steps up to here but when i try to see get data , i have this error message:
HTTP Error 404.0 – Not Found
The resource you are looking for has been deleted, has been renamed, or is temporarily unavailable.
have i missing something ?
Any help
Hi Taiseer,
First I’d like to say thanks a lot, I am learning so much from you here..
Second I have a problem and cant get passed it.
By this point (End of part 3), if I run the application, the chrome opens with the URL:
http://localhost:46040/ with an error (HTTP 404)
I am adding this to the URL:
http://localhost:46040/api/users (I have users instead of courses)
Then when the repository.GetAllUsers is called, it throws an Exception:
An exception of type ‘System.ArgumentException’ occurred in HiveKnows.Web.dll but was not handled in user code
Additional information: Object of type ‘HiveKnows.Data.HiveKnowsContext’ cannot be converted to type ‘System.Data.Entity.IDatabaseInitializer`1[HiveKnows.Data.HiveKnowsContext]
Any help will be appreciated.. 🙂
Thanks,
Eibi
Hi Eibi,
HiveKnows is internal implement you have built, you have casting issues between 2 types, I can’t figure out the issue but double check the data type of the objects you are trying to cast.
Thanks for the reply Taiseer,
Sorry, I’ve made some changes along the way to adapt to my needs.. 😉
HiveKnows.Data is just the namespace, all other objects are same as yours:
HiveKnows.Data.HiveKnowsContext = LearningContext
My code is exactly parallel to yours (I hope)
I really cant find a different between my code and yours, what could be the casting issue. what is the IDatabaseInitializer got to do with it?
Furthermore, my Seed() method is not being called and my database is not created. Is there an explanation for this? could it be because of the above exception?
I appreciate the help..
Thanks!
The seed method should be invoked when you .NET code tries to execute something against your database, make sure you are setting the right initialization strategy for DB context.
Sorry but I do not know what is the issue for your custom code.