This kata is designed to introduce you to creating a web API using ASP.Net Core.
https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-2.1
- verify dotnet version
dotnet --version
is higher than 2.0.0
In this kata you will create a web API that can return a list of products
In a terminal:
- Navigate to
.\app-website-1
- run
dotnet new webapi -n "ProductsApi"
cd ProductsApi
- run
dotnet restore
- run
dotnet build
- run
dotnet run
- Navigate to
http://localhost:5000/api/Values
to check the project template works
You can debug in Visual Studio by opening the .csproj
file
When saving files you will be prompted to create a .sln
and a launch settings file. Delete the launch settings file as you only want the .sln
file.
You can now run by debugging in VS, or from the command line.
Delete auto generated ValuesController
and add new class ProductsController
with contents:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace ProductsApi.Controllers
{
[Route("api/[controller]")]
public class ProductsController : Controller
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "SQL Source Control" };
}
}
}
Navigate to http://localhost:5000/api/Products
to check it works. You should see SQL Source Control
in your browser.
Note: Remember to restart the server each time you change the code
Create a new folder called Model
In Model
create a new class, Product
, with properties:
public string Name { get; }
public string Description { get; }
Alter Get()
in ProductsController
to return an IEnumerable<Product>
Navigate to http://localhost:5000/api/Products
to check it works
Amend ProductsController
to maintain a collection of products
Create a parameter-less constructor for ProductsController
and initialize your collection
Return your collection in Get()
Navigate to http://localhost:5000/api/Products
to check it works
Create a new method on ProductsController
to handle Post requests:
[HttpPost]
public void Post([FromBody] Product value) { }
Add logic to add the product from the post request to your collection
In order to test a POST request you'll need some tooling. I recommend Postman: https://www.getpostman.com/
Send a POST request to http://localhost:5000/api/Products
with a Content-Type of application/json
and body:
{
"Name": "Readyroll",
"Description": "Ready to roll"
}
Navigate to http://localhost:5000/api/Products
to check it works
This is because controllers are instantiated per request
In order to persist data across requests we need to use dependency injection
First create a new directory Store
, and a class, ProductStore
Move the logic for persisting the Product
collection to ProductStore
e.g. it should have a contract like:
public IEnumerable<Product> GetAll();
public void Add(Product product);
Now make ProductsController
depend on the new ProductStore
by passing it in the constructor and assigning it to a field
Finally, in Startup
, find ConfigureServices()
and add the following line:
services.AddSingleton<ProductStore>();
This will register ProductStore
with the dependency injection system
Because we're using singleton scope, all controller instances that ask for a product store will now have access to the same one
Now make the POST request again, and navigate to http://localhost:5000/api/Products
to see if it works
See https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection?view=aspnetcore-2.1 for more details about dependency injection
Look at the signature for Post
in ProductsController
again
public void Post([FromBody] Product value)
Notice the [FromBody]
attribute. This means the value will be automatically parsed from JSON in the request body into the C# type. This process is known as 'Parameter binding'
Parameters can be bound from the request URI also.
For instance, given:
GET http://localhost:5000/api/Products/SQL%20Source%20Control
Name
can be bound to an argument for your Get
method
On ProductsController
change the signature for Get
to take a string name
. e.g:
public IEnumerable<Product> Get(string name)
At the top of ProductsController
and a new Route
attribute to match part of the URI to the name
argument in Get
[Route("api/[controller]/{name}")]
If no name is specified in the URI, then name
will be null
Add logic to return products by name if the supplied name
isn't null
Add a new product using a POST request, then search for it by name: e.g: GET http://localhost:5000/api/Products/SQL%20Source%20Control
Note: the routes are case sensitive