HTTP and Web APIs

A Web API serves data instead of pages. You will look closely at HTTP requests and responses, build minimal APIs and controller-based APIs with ASP.NET Core, use async and await, meet Entity Framework Core, and handle errors in one place.

Updated

Dissecting the Request and Response

  1. The Request / Response Pattern (Journeyman): A web request and its response are simple text messages, each made of a header and an optional body.
  2. .http Files in Visual Studio (Journeyman): An .http file holds requests written as plain text, one after another, separated by ###. Visual Studio sends each one with a click and shows the response's status, headers and body, so you can test an API without leaving your project.

HTTP Status Codes

  1. HTTP Status Codes: an overview (Apprentice): Every response starts with a status code that says how the request went. By default an ASP.NET Core API answers 200 when an action returns, and 500 when it throws, so an action has to choose a better code, such as 404, on purpose.
  2. Common HTTP Status Codes (Apprentice): The status codes a REST API uses most, 200, 201, 204, 400, 401, 403, 404 and 500, and what each tells the client.

Minimal APIs

  1. Your First Minimal API (Apprentice): A minimal API maps each address and method straight to a lambda in Program.cs, with MapGet, MapPost, MapPut and MapDelete. The lambda's parameters come from the route and the body, and Results.NotFound(), Results.Created() and friends choose the status code.

Web APIs with Controllers

  1. Web APIs with Controllers: an overview (Apprentice): REST is the common way to design a web API. Each kind of data is a resource with its own address, and the HTTP method says what to do with it. GET, POST, PUT and DELETE match SELECT, INSERT, UPDATE and DELETE, each with its usual status code.
  2. Creating an API Controller (Journeyman): An API controller derives from ControllerBase, is marked [ApiController], and gets its address from [Route]. It asks for its DAO through an interface registered in Program.cs, and its actions return data that ASP.NET Core sends as JSON.
  3. GET (Journeyman): GET reads data. One [HttpGet] action returns the whole collection, and an [HttpGet("{id}")] action returns one item, its id taken from the address. ASP.NET Core sends what they return as JSON, with 200 OK.
  4. POST (Journeyman): POST creates a new item from the JSON in the request body. The DAO inserts it and returns it with its new id, and the action answers 201 Created with CreatedAtAction, which also says where the new item lives. Invalid bodies get 400 automatically.
  5. PUT (Journeyman): PUT replaces an item with the one in the request body, its id taken from the address. The DAO updates the row and hands back the category as it was saved, and the action answers 200 OK with it, or 404 Not Found when there's no such item.
  6. DELETE (Journeyman): DELETE removes the item at an address. It sends no body; the action deletes by the id from the route and answers 204 No Content, or 404 Not Found when there was nothing to delete.
  7. Returning ActionResult<T> (Journeyman): ActionResult<T> lets an action return either its data or a status code result, such as NotFound(). Return the item to answer 200 with it, or a helper such as NotFound(), BadRequest() or Conflict() to answer with that status instead.

async and await in C#

  1. Tasks, async and await (Journeyman): A Task is work that finishes later. An async method can await a task, which frees its thread while it waits for a database or the network, so a web server can keep serving other requests. Await every async call, and name async methods with Async.

Entity Framework Core

  1. What is an ORM? (Journeyman): An ORM (object-relational mapper) moves data between C# objects and database tables. Dapper is a micro-ORM, where you write the SQL and it fills the objects. Entity Framework Core is a full ORM, where you write LINQ and it writes the SQL, and also tracks and saves your changes.
  2. DbContext and Entities (Journeyman): EF Core maps entity classes to tables, matching names by convention. A DbContext class lists a DbSet for each table and holds the connection. In ASP.NET Core, register it with AddDbContext and UseSqlServer, and controllers ask for it like any other service.
  3. Querying and Saving with EF Core (Journeyman): Query a DbSet with LINQ, finished by an async method such as ToListAsync, and EF Core runs it as SQL. Add, change or remove entities, then call SaveChangesAsync, and EF Core writes the INSERT, UPDATE or DELETE for you.
  4. Dapper or EF Core? (Journeyman): Dapper gives full control of the SQL with very little to learn; EF Core writes the SQL, tracks changes and follows relationships. Choose Dapper when the SQL matters most and EF Core when the objects do, and keep to one per project unless there's a reason.

Handling Global Exceptions in ASP.NET Core

  1. Handling Global Exceptions in ASP.NET Core (Freelancer): AddProblemDetails and UseExceptionHandler catch every exception an action doesn't, log it, and answer with a tidy 500 problem details response that never shows the stack trace. An IExceptionHandler can answer chosen exceptions with a better status, such as 409 Conflict.

Exercise: Northwind Web API

  1. Exercise: Northwind Web API (Journeyman): Build Northwind's products API. Write an async controller with GET (all, by category, by id), POST, PUT and DELETE, each answering the right status code, and set up Program.cs to register the DAO and handle errors in one place.