Creating Web APIs in ASP.NET Core is very straightforward. You create controllers that have 3 things:
[Route("someUrl/[controller]")]
.The controller of a Web API looks like:
[ApiController]
[Route("someURL/[controller]")]
public class ExampleController : ControllerBase
Page Contents
The reason why you must not create a Web API controller by deriving from the Controller class is because the Controller class derives from ControllerBase class and adds support for views, so it’s for handling web pages, not web API requests. There’s an exception to this rule: if you plan to use the same controller for both views and web APIs, then only derive it from Controller class.
The ControllerBase class provides many properties and methods that are useful for handling HTTP requests. Some of these are:
Name | Description |
---|---|
BadRequest | Returns 400 status code. |
Ok | Returns a 200 status code along with the result object. |
NotFound | Returns 404 status code. |
PhysicalFile | Returns a file. |
This tutorial is a part of the ASP.NET Core API series which contains 4 tutorials to master this area:
Create a new project in Visual Studio, use ASP.NET Core Web Application (.NET Core) template to create a new Empty Project and name it APIControllers. Remember to select the framework as .NET Core and version as ASP.NET Core 3.1.
Create Models folder in the root of the application and add a class called Reservation.cs to it. Add the following code to this class:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace APIControllers.Models { public class Reservation { public int Id { get; set; } public string Name { get; set; } public string StartLocation { get; set; } public string EndLocation { get; set; } } }
Next add a file called IRepository.cs to the Models folder and used it to define an interface as shown in the below code.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace APIControllers.Models { public interface IRepository { IEnumerable<Reservation> Reservations { get; } Reservation this[int id] { get; } Reservation AddReservation(Reservation reservation); Reservation UpdateReservation(Reservation reservation); void DeleteReservation(int id); } }
Finally add a class file called Repository.cs to the Models folder and used it to define a non-persistent store of reservations, by implementation of the IRepository interface.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace APIControllers.Models { public class Repository: IRepository { private Dictionary<int, Reservation> items; public Repository() { items = new Dictionary<int, Reservation>(); new List<Reservation> { new Reservation {Id=1, Name = "Ankit", StartLocation = "New York", EndLocation="Beijing" }, new Reservation {Id=2, Name = "Bobby", StartLocation = "New Jersey", EndLocation="Boston" }, new Reservation {Id=3, Name = "Jacky", StartLocation = "London", EndLocation="Paris" } }.ForEach(r => AddReservation(r)); } public Reservation this[int id] => items.ContainsKey(id) ? items[id] : null; public IEnumerable<Reservation> Reservations => items.Values; public Reservation AddReservation(Reservation reservation) { if (reservation.Id == 0) { int key = items.Count; while (items.ContainsKey(key)) { key++; }; reservation.Id = key; } items[reservation.Id] = reservation; return reservation; } public void DeleteReservation(int id) => items.Remove(id); public Reservation UpdateReservation(Reservation reservation) => AddReservation(reservation); } }
The Repository class creates 3 set of reservation objects when it is instantiated, and since there is no persistent storage, any changes will be lost when the application is stopped or restarted.
The Class contains methods and properties to do CRUD Operations and all the reservations are stored in a Dictionary<int, Reservation>
type object. These methods and properties are:
To enable the MVC Framework and the middleware components required for development, make the changes to the Startup.cs file as shown in the code below.
I used the AddSingleton method to set up the service mapping for the reservation repository.
The code for the Startup.cs class is given below:
using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using APIControllers.Models; namespace APIControllers { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IRepository, Repository>(); services.AddControllersWithViews(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
To make it easier to remember, change the App URL port to 8888. You can do it by right clicking the project in the Solution Explorer and select Properties. On the window that opens, select the Debug tab, where you will find the App URL field.
Change the port to 8888 and un-select the Enable SSL option. Then press Ctrl+S to save the changes.
See the below image:
Now comes the most important part of creating a Controller for the Web API. Remember that this Controller is just a normal Controller, that allows data in the model to be retrieved or modified, and then deliver it to the client. It does this without having to use the actions provided by the regular controllers.
The data delivery is done by following a pattern known by name as REST. REST Stands for REpresentational State Transfer pattern, which contains 2 things:
First add the package called Microsoft.AspNetCore.JsonPatch from NuGet. This is needed to support JSON Patch. I have shown this package in the below image.
Next create Controllers folder on the root of the project and to it add a new Controller called ReservationController.cs. Also add the following code to it.
using System; using System.Collections.Generic; using APIControllers.Models; using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; using System.Linq; using Microsoft.Extensions.Primitives; namespace APIControllers.Controllers { [ApiController] [Route("api/[controller]")] public class ReservationController : ControllerBase { private IRepository repository; public ReservationController(IRepository repo) => repository = repo; [HttpGet] public IEnumerable<Reservation> Get() => repository.Reservations; [HttpGet("{id}")] public ActionResult<Reservation> Get(int id) { if (id == 0) return BadRequest("Value must be passed in the request body."); return Ok(repository[id]); } [HttpPost] public Reservation Post([FromBody] Reservation res) => repository.AddReservation(new Reservation { Name = res.Name, StartLocation = res.StartLocation, EndLocation = res.EndLocation }); [HttpPut] public Reservation Put([FromBody] Reservation res) => repository.UpdateReservation(res); [HttpPatch("{id}")] public StatusCodeResult Patch(int id, [FromBody]JsonPatchDocument<Reservation> patch) { Reservation res = Get(id); if (res != null) { patch.ApplyTo(res); return Ok(); } return NotFound(); } [HttpDelete("{id}")] public void Delete(int id) => repository.DeleteReservation(id); } }
Notice the Controller derives from ControllerBase class and has an attribute called [ApiController] applied to it. The Controller gets the Reservation class object through the Dependency Injection feature.
The route by which this controller can be reached is defined by Attribute Routes. You can check my tutorial called Learn Attribute Routing in ASP.NET Core to know it in full details.
This Web API Controller is reached through the URL – http://localhost:8888/api/Reservation.
[ApiController] [Route("api/[controller]")] public class ReservationController : ControllerBase { ... }
Now test this thing by running the application and then opening the URL – http://localhost:8888/api/Reservation on the browser. You will see the JSON of the 3 reservations as shown in the below image.
The URL calls the Get method of the Reservation controller. This method is shown below:
[HttpGet]
public IEnumerable<Reservation> Get() => repository.Reservations;
As you can see this method returns all the reservations so you get the JSON of the reservations on the browser.
It should be noted that by default ASP.NET CORE does the following things:
The Web API Controller action methods have been applied some HTTP attributes. So they are invoked only by the specific HTTP method known as VERBS.
Examples of VERBS are – GET, POST, PUT, PATCH, DELETE and HEAD.
Name | Description |
---|---|
HttpGet | It specifies that the action method can be invoked only by HTTP requests that use the GET verb. |
HttpPost | It specifies that the action method can be invoked only by HTTP requests that use the POST verb. |
HttpDelete | It specifies that the action method can be invoked only by HTTP requests that use the DELETE verb. |
HttpPut | It specifies that the action method can be invoked only by HTTP requests that use the PUT verb. |
HttpPatch | It specifies that the action method can be invoked only by HTTP requests that use the PATCH verb. |
HttpHead | It specifies that the action method can be invoked only by HTTP requests that use the HEAD verb. |
Note – to specify multiple attributes use the attribute called AcceptVerbs.
There are two [HttpGet] methods that will be invoked on HTTP request of type GET. These methods work in this way.
[HttpGet] public IEnumerable<Reservation> Get() => repository.Reservations;
http://localhost:8888/api/Reservation/1
http://localhost:8888/api/Reservation/2
http://localhost:8888/api/Reservation/3
etc
This action method is shown below.
[HttpGet("{id}")] public ActionResult<Reservation> Get(int id) { if (id == 0) return BadRequest("Value must be passed in the request body."); return Ok(repository[id]); }
If the user does not sends an id which in that case the id gets the default value of 0. So I am returning BadRequest() for the response.
The HTTP GET requests can be made directly from the browser. Run your application and go to the URL http://localhost:8888/api/Reservation, where you will see a JSON containing all the reservations, as shown in the image below:
Similarly if you go to the URL – http://localhost:8888/api/Reservation/1 in your browser, then you will get the JSON for the first Reservation, as shown by the image below:
The HttpPost Action method is used to create a new Reservation. It receives the Reservation object in it’s argument. The [FromBody] attribute applied to it’s argument ensures the body content send from the client will be decoded, and put to this argument, using the Model Binding concept of ASP.NET Core.
The URL to invoke this Action method is – http://localhost:8888/api/Reservation. Note that although it’s URL is same as that of the GET Action, the presence of [HttpPost] attribute ensures that it is invoked only for HTTP request of type POST.
This action returns the newly added Reservation object in JSON format. The reservation object also contains the value of the created Id field. This method is given below.
[HttpPost] public Reservation Post([FromBody] Reservation res) => repository.AddReservation(new Reservation { Name = res.Name, StartLocation = res.StartLocation, EndLocation = res.EndLocation });
The HttpPut Action is used for doing the update of a Reservation object. It will be invoked when Http request of type PUT is made to the URL – http://localhost:8888/api/Reservation. This method is given below.
[HttpPut] public Reservation Put([FromForm] Reservation res) => repository.UpdateReservation(res);
The [FromForm] attribute on the argument ensure that the form data sent by the client will be used to bind this Reservation object using Model Binding.
This action method returns the Updated Reservation object in JSON format.
The HttpDelete action deletes a reservation from the repository. This method is called when Http request of type DELETE is initiated on the URLs given below –
http://localhost:8888/api/Reseration/1
http://localhost:8888/api/Reseration/2
http://localhost:8888/api/Reseration/3
etc
Note: The id of the reservation to be deleted is passed as the 3rd segment of the URL. This method is shown below.
[HttpDelete("{id}")] public void Delete(int id) => repository.DeleteReservation(id);
The HttpPatch Action can do multiple operations, like Add, Removing, Updating, Copying, etc, of a Reservation object which is sent from the client. Here the client only sends a specific set of Reservation properties instead of the whole reservation object to the API in JSON.
This JSON format looks like:
[ { "op": "replace", "path": "Name", "value": "Ram"}, { "op": "replace", "path": "StartLocation", "value": "Moscow"} ]
The JSON has op property which specifies the type of operation, and a path property which specifies where the operation will be applied. The value property specifies it’s new value.
ASP.NET Core will automatically process the JSON data and sends it to the action method as a JsonPatchDocument<T> object, where T is the type of the model object to be modified (here it is the Reservation object).
The JsonPatchDocument object is then used to modify an object from the repository using the ApplyTo() method. See it’s code below.
[HttpPatch("{id}")] public StatusCodeResult Patch(int id, [FromBody]JsonPatchDocument<Reservation> patch) { Reservation res = Get(id).Value; if (res != null) { patch.ApplyTo(res); return Ok(); } return NotFound(); }
If you are creating the Web API in ASP.NET Core version 3 and above then you must know that AddNewtonsoftJson replaces the System.Text.Json-based input and output formatters used for formatting all JSON content. Therefore you have to enable JSON Patch support in your API project.
services.AddControllersWithViews().AddNewtonsoftJson();
The table given below summarizes the working details for each of these action methods:
HTTP Request Type | URL | Data from Client | Returns |
---|---|---|---|
GET | /api/Reservation | No data | Returns all the reservations in JSON |
GET | /api/Reservation/1, /api/Reservation/2, etc | No data | Returns the reservation data of the id which is sent to its parameter in JSON |
POST | /api/Reservation | The Reservation object in JSON. | Returns the newly created Reservation object in JSON |
PUT | /api/Reservation | The Reservation object in JSON. | Returns the newly updated Reservation object in JSON |
DELETE | /api/Reservation/1, /api/Reservation/2, etc | No data | None |
PATCH | /api/Reservation/1, /api/Reservation/2, etc | A JSON that contains set of modifications to be applied. | Returns confirmation that the changes have been applied. |
The link to download the full source code of this tutorial is given below:
In this way the API is created in ASP.NET Core. In the next tutorial I will consume this API, link is – How to Consuming a Web API in ASP.NET Core.
Share this article -