Module Goal: Build a simple API that returns JSON and XML.
- In Visual Studio File-> New Project -> .NET Core -> ASP.NET Core Web Application -> Web API
Or you can use the dotnet CLI
dotnet new webapi
Add a Model
- Create a folder called
Models
and create a class calledProducts
in that folder:
namespace Products.Models
{
public class Products
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Add a Controller
3. In the Controller folder create a class called ProductsController
.
4. Add an attribute route [Route("/api/[controller]")]
to the ProductsController
class:
[Route("/api/[controller]")]
public class ProductsController
{
}
- Add a
Get
method toProductsController
that returns astring
"Hello API World" with an attribute route
[Route("/api/[controller]")]
public class ProductsController
{
[HttpGet]
public string Get() => "Hello World";
}
- Run the application and navigate to
/api/products
, it should return the string "Hello World".
- Add the
Microsoft.AspNetCore.Mvc.Formatters.Json
To edit your csproj in Visual Studio: Right click on your application name and select edit csproj
Option 1: Edit by hand
<ItemGroup>
......
<PackageReference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" Version="1.1.1" />
....
</ItemGroup>
Option 2: Use NuGet package manager: Search for Microsoft.AspNetCore.Mvc.Formatters.Json
- Configure MVC to use the JSON formatter by changing the
ConfigureServices
inStartup.cs
to use the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters();
}
- Add a static list of projects to the
ProductsController
:
public class ProductsController : ControllerBase
{
private static List<Product> _products = new List<Product>(new[] {
new Product() { Id = 1, Name = "Green Peppers" },
new Product() { Id = 2, Name = "Tacos" },
new Product() { Id = 3, Name = "Chipotle Sauce" },
});
...
}
- Change the
Get
method inProductsController
to returnIEnumerable<Product>
and return the list of products.
public IEnumerable<Product> Get()
{
return _products;
}
- Run the application and navigate to
/api/products
. You should see a JSON payload of with all of the products. - Make a POST request to
/api/products
, you should see a JSON payload with all products. - Restrict the
Get
method to respond to GET requests by adding an[HttpGet]
attribute to the method:
[HttpGet]
public IEnumerable<Product> Get()
{
return _products;
}
- Add a
Get
method to theProductsController
that takes anint id
parameter and returnsProduct
.
public Product Get(int id)
{
return _products.SingleOrDefault(p => p.Id == id);
}
- Add an
HttpGet
route specifying theid
as a route parameter:
[HttpGet("{id}")]
public Product Get(int id)
{
return _products.SingleOrDefault(p => p.Id == id);
}
- Run the application, and navigate to
/api/products/1
, you should see a JSON response for the first product. - Navigate to
/api/products/25
, it should return a 204 status code. - Change the
Get
method in theProductsController
to return a 404 if the product search returns null.
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var product = _products.SingleOrDefault(p => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
- Run the application and navigate to
/api/products/40
and it should return a 404 status code.
- Add a
Post
method toProductsController
the takes aProduct
as input and adds it to the list of products:
public void Post(Product product)
{
_products.Add(product);
}
- Add an
[HttpPost]
attribute to the method to constrain it to the POST HTTP verb:
[HttpPost]
public void Post(Product product)
{
_products.Add(product);
}
- Add a
[FromBody]
to theproduct
argument:
[HttpPost]
public void Post([FromBody]Product product)
{
_products.Add(product);
}
- Run the application and post a JSON payload with the
Content-Type
headerapplication/json
to/api/products
:
{
"Id" : "4",
"Name": "4K Television"
}
- Make a GET request to
/api/products
and the new entity should show up in the list. - Change the
Post
method to return anIActionResult
with a 201 status code and the addedProduct
:
[HttpPost]
public IActionResult Post([FromBody]Product product)
{
_products.Add(product);
return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
}
- Add another product to the list by posting to
/api/products
:
{
"Id": "5",
"Name": "Radio"
}
- It should return a 201 and the
Product
that was added as JSON.
-
Add the
Microsoft.AspNetCore.Mvc.DataAnnotations
package tocsproj
: -
In
Startup.cs
add a call toAddDataAnnotations()
chained off theAddMvcCore
method inConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddDataAnnotations();
}
- Modify the
Product.cs
file and add a[Required]
attribute to the name property:
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
- In
ProductsController.cs
modify thePost
method and add aModelState.IsValid
check. If the model state is not valid, return a 400 response to the client:
[HttpPost]
public IActionResult Post([FromBody]Product product)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
_products.Add(product);
return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
}
- POST an empty JSON payload to
/api/products
and it should return a 400 response:
{}
- Modify the
Post
method to return the validation errors to the client by passing theModelState
object to theBadRequest
method:
[HttpPost]
public IActionResult Post([FromBody]Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_products.Add(product);
return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
}
- POST an empty JSON payload to
/api/products
and it should return a 400 response with the validation errors formatted as JSON.
-
Add the
Microsoft.AspNetCore.Mvc.Formatters.Xml
package tocsproj
: -
In
Startup.cs
add a call toAddXmlDataContractSerializerFormatters()
chained off theAddMvcCore
method inConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddJsonFormatters()
.AddXmlDataContractSerializerFormatters();
}
- Run the application and make a request to
/api/products
with the accept headerapplication/xml
. The response should be an XML payload of the products.
- Add a
[Produces("application/json")]
attribute to theProductsController
class:
[Produces("application/json")]
[Route("/api/[controller]")]
public class ProductsController : ControllerBase
- Add model validation when the product has a missing Name (and return that back to the client)
- Make the JSON properties camel case
- Write a custom output formatter to prints the product name as plain text
- Replace the static list of products with entity framework in memory store
- Replace the static list with entity framework + sqlite