Exercise - Add a controller
A controller is a public class with one or more public methods known as actions. By convention, a controller is placed in the project root's Controllers directory. The actions are exposed as HTTP endpoints inside the web API controller.
Create a controller
Select the Controllers folder in Visual Studio Code and add a new file called PizzaController.cs.
An empty class file named PizzaController.cs is created in the Controllers directory. The Controllers directory name is a convention. The directory name comes from the model-view-controller architecture that the web API uses.
Note
By convention, controller class names are suffixed with Controller.
Add the following code to Controllers/PizzaController.cs. Save your changes.
using ContosoPizza.Models; using ContosoPizza.Services; using Microsoft.AspNetCore.Mvc; namespace ContosoPizza.Controllers; [ApiController] [Route("[controller]")] public class PizzaController : ControllerBase { public PizzaController() { } // GET all action // GET by Id action // POST action // PUT action // DELETE action }
As you learned previously, this class derives from
ControllerBase
, the base class for working with HTTP requests in ASP.NET Core. It also includes the two standard attributes you learned about:[ApiController]
and[Route]
. As before, the[Route]
attribute defines a mapping to the[controller]
token. Because this controller class is namedPizzaController
, this controller handles requests tohttps://localhost:{PORT}/pizza
.
Get all pizzas
The first REST verb that you need to implement is GET
, where a client can get all pizzas from the API. You can use the built-in [HttpGet]
attribute to define a method that returns the pizzas from our service.
Replace the // GET all action
comment in Controllers/PizzaController.cs with the following code:
[HttpGet]
public ActionResult<List<Pizza>> GetAll() =>
PizzaService.GetAll();
The preceding action:
- Responds only to the HTTP
GET
verb, as denoted by the[HttpGet]
attribute. - Returns an
ActionResult
instance of typeList<Pizza>
. TheActionResult
type is the base class for all action results in ASP.NET Core. - Queries the service for all pizza and automatically returns data with a
Content-Type
value ofapplication/json
.
Retrieve a single pizza
The client might also want to request information about a specific pizza instead of the entire list. You can implement another GET
action that requires an id
parameter. You can use the built-in [HttpGet("{id}")]
attribute to define a method that returns the pizzas from our service. The routing logic registers [HttpGet]
(without id
) and [HttpGet("{id}")]
(with id
) as two different routes. You can then write a separate action to retrieve a single item.
Replace the // GET by Id action
comment in Controllers/PizzaController.cs with the following code:
[HttpGet("{id}")]
public ActionResult<Pizza> Get(int id)
{
var pizza = PizzaService.Get(id);
if(pizza == null)
return NotFound();
return pizza;
}
The preceding action:
- Responds only to the HTTP
GET
verb, as denoted by the[HttpGet]
attribute. - Requires that the
id
parameter's value is included in the URL segment afterpizza/
. Remember, the controller-level[Route]
attribute defined the/pizza
pattern. - Queries the database for a pizza that matches the provided
id
parameter.
Each ActionResult
instance used in the preceding action is mapped to the corresponding HTTP status code in the following table:
ASP.NET Core action result |
HTTP status code | Description |
---|---|---|
Ok is implied |
200 | A product that matches the provided id parameter exists in the in-memory cache.The product is included in the response body in the media type, as defined in the accept HTTP request header (JSON by default). |
NotFound |
404 | A product that matches the provided id parameter doesn't exist in the in-memory cache. |
Build and run the new controller
Build and start the web API by running the following command:
dotnet run
Test the controller with an Http file
Open ContosoPizza.http
Add a new GET to call the
Pizza
endpoint under the ### seperator:GET {{ContosoPizza_HostAddress}}/pizza/ Accept: application/json ###
Select the Send Request command above this new GET call.
The preceding command returns a list of all pizzas in JSON:
HTTP/1.1 200 OK Connection: close Content-Type: application/json; charset=utf-8 Date: Wed, 17 Jan 2024 16:57:09 GMT Server: Kestrel Transfer-Encoding: chunked [ { "id": 1, "name": "Classic Italian", "isGlutenFree": false }, { "id": 2, "name": "Veggie", "isGlutenFree": true } ]
To query for a single pizza, you can make another
GET
request, but pass in anid
parameter by using the following command:GET {{ContosoPizza_HostAddress}}/pizza/1 Accept: application/json ###
The preceding command returns
Classic Italian
with the following output:HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 02 Apr 2021 21:57:57 GMT Server: Kestrel Transfer-Encoding: chunked { "id": 1, "name": "Classic Italian", "isGlutenFree": false }
Our API also handles situations where the item doesn't exist. Call the API again, but pass in an invalid pizza
id
parameter by using the following command:GET {{ContosoPizza_HostAddress}}/pizza/5 Accept: application/json ###
The preceding command returns a
404 Not Found
error with the following output:HTTP/1.1 404 Not Found Content-Type: application/problem+json; charset=utf-8 Date: Fri, 02 Apr 2021 22:03:06 GMT Server: Kestrel Transfer-Encoding: chunked { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "traceId": "00-ec263e401ec554b6a2f3e216a1d1fac5-4b40b8023d56762c-00" }
Now that you finished implementing the GET
verbs. In the next unit, you can add more actions to PizzaController
to support CRUD operations on pizza data.
Optional: Test the controller with Command Line HTTP Read-Eval-Print Loop (REPL)
Open the existing
httprepl
terminal, or open a new integrated terminal from Visual Studio Code by selecting Terminal > New Terminal from the main menu.Connect to our web API by running the following command:
httprepl https://localhost:{PORT}
Alternatively, run the following command at any time while
HttpRepl
is running:connect https://localhost:{PORT}
To see the newly available
Pizza
endpoint, run the following command:ls
The preceding command detects all APIs available on the connected endpoint. It should display the following code:
https://localhost:{PORT}/> ls . [] Pizza [GET] WeatherForecast [GET]
Go to the
Pizza
endpoint by running the following command:cd Pizza
The preceding command shows an output of available APIs for the
Pizza
endpoint:https://localhost:{PORT}/> cd Pizza /Pizza [GET]
Make a
GET
request inHttpRepl
by using the following command:get
The preceding command returns a list of all pizzas in JSON:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 02 Apr 2021 21:55:53 GMT Server: Kestrel Transfer-Encoding: chunked [ { "id": 1, "name": "Classic Italian", "isGlutenFree": false }, { "id": 2, "name": "Veggie", "isGlutenFree": true } ]
To query for a single pizza, you can make another
GET
request, but pass in anid
parameter by using the following command:get 1
The preceding command returns
Classic Italian
with the following output:HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 02 Apr 2021 21:57:57 GMT Server: Kestrel Transfer-Encoding: chunked { "id": 1, "name": "Classic Italian", "isGlutenFree": false }
Our API also handles situations where the item doesn't exist. Call the API again, but pass in an invalid pizza
id
parameter by using the following command:get 5
The preceding command returns a
404 Not Found
error with the following output:HTTP/1.1 404 Not Found Content-Type: application/problem+json; charset=utf-8 Date: Fri, 02 Apr 2021 22:03:06 GMT Server: Kestrel Transfer-Encoding: chunked { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "traceId": "00-ec263e401ec554b6a2f3e216a1d1fac5-4b40b8023d56762c-00" }
Return to the
dotnet
terminal in the drop-down list in Visual Studio Code and shut down the web API by selecting CTRL+C on your keyboard.
Now that you finished implementing the GET
verbs. In the next unit, you can add more actions to PizzaController
to support CRUD operations on pizza data.