Web API Best Practices
Web API Best Practices
STEVE SMITH
ARDALIS.COM | @ARDALIS | STEVE@DEVIQ.COM
DEVIQ.COM
Learn More After Today
1) DevIQ
◦ ASP.NET Core Quick Start http://aspnetcorequickstart.com DEVINTFALL17 20% OFF!
REST-based APIs are stateless; each request may be handled by a different server-side resource
https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design
URI Design Considerations
URI values should correspond to nouns
◦ E.g. /customers, /authors, /orders
Keep it SIMPLE
Keep it CONSISTENT
Don’t Expose Business/Data Model
Avoid coupling your Web API directly to your data model
API design, including URIs, may not may 1:1 to domain objects or database tables.
Example:
POST /orders
May map to a NewOrderRequest on the server that triggers processing payment, checking
inventory, sending notifications, etc.
Or it could just insert a record in the Orders table.
It should be able to do either without the API changing.
Use a standard URI structure for
subcollections
For performance or other reasons, might not return full object tree with root-level request:
GET /customers/1
{
“id”:”1”,
“name”: “Steve Smith”
}
To get the customer’s orders:
GET /customers/1/orders
[{“id”:”123”,”customerId”:”1”, …}, {“id”:”234”,”customerId”:”1”, …}]
Avoid Deeply Nested URI Structures
OK
/customers
/customers/1
/customers/1/orders
TOO MUCH
/customers/1/orders/123 (instead: /orders/123)
/customers/1/orders/123/items/1/products/2 (instead: /products/2)
Hypertext as the Engine of Application State
(HATEOAS)
Less commonly implemented aspect of REST approach
Currently no standards or specifications defining implementation
Basic idea: Each response includes links defining available requests on a given resource
Example:
GET /customers/1
Response includes customer data, as well as links to:
Update the customer Delete the customer List customer orders
List customer addresses Add an address Add an order
Standard Verbs and Behaviors
GET Fetch a resource (or collection of resources)
PUT Update a resource.
POST Create a new resource.
DELETE Delete a resource.
Safe and Idempotent API Requests
Safe requests are requests that do not change resources, and which can be made repeatedly
without impact. Think of safe requests as read-only operations.
An idempotent HTTP method can be called multiple times without changing the expected
response.
*Decide if a DELETE for a missing id should return a 404 or not. If so, then it won’t be Idempotent.
http://restcookbook.com/HTTP%20Methods/idempotency/
Web API
Implementation
Use Model Validation
Always check if Model.IsValid before performing unsafe operations
Use Filters To Represent Policies
Validate Model State using a filter (globally, per-controller, or per-action)
Use Proper HTTP Status Codes as Results
200 OK Request was successful; body has response.
201 OK POST or PUT was successful; body has latest representation.
204 OK DELETE was successful; resource was deleted.
400 BAD REQUEST The request was invalid or cannot otherwise be served.
401 UNAUTHORIZED Authorization failed or authentication details not supplied.
404 NOT FOUND The URI requested or the resource requested doesn’t exist.
500 Internal Server Error Something very bad happened. Unhandled exceptions lead to this.
Prefer NotFound to
NullReferenceException
Prefer NotFound to
NullReferenceException
Use a filter to confirm existence
Avoid Duplicating Data within Requests
Don’t ask for an ID in the route and also in the BindingModel
◦ Unless you’re going to allow updates to a resource’s ID!
Be careful to avoid creating DTO types that inadvertently reference non-DTO types.
◦ Look for using statements in your DTO files that shouldn’t be there
If specifying ID on DTOs, may not make sense to use for new object requests (POSTs)
◦ Consider having separate NewResourceDTO and ResourceDTO types
◦ ResourceDTO can inherit from NewResourceDTO and simply add the Id property
Non-DTOs May Expose Sensitive Data
Post-Redirect-Get (PRG) Pattern
Overview
◦ Client POSTs to Server
◦ Server performs requested operation and returns a Redirect (302) to new URI
◦ Client GETs new URI
REST services should (typically) return the resource in the body of POST commands
What to Return?
Object
◦ Author, Customer, or void
◦ Automatically wrapped in a result (or
Encoding-Specific
◦ return Json(model); // JsonResult
IActionResult
◦ return Ok(model);
◦ return NotFound();
◦ return BadRequest();
Prefer IActionResult;
Support Content Negotiation
Requests can include Accept header specifying content they want/support
Integration Tests
◦ Test several methods and/or classes working together
◦ Useful for verifying infrastructure code works correctly
Functional Tests
◦ Test full application stack
◦ Slowest, often most brittle, but provide greatest confidence a particular user scenario works fully
Test APIs with TestServer
Install Microsoft.AspNetCore.TestHost Nuget Package
Configure with WebHostBuilder; use HttpClient to make requests to TestServer instance.
Example Web API Test
Demo
VALIDATING FILTERS PRODUCE SAME RESULTS AS INLINE CODE
Versioning Web APIs
No Versioning
Limit updates to non-destructive wherever possible
Consider whether you will version your entire API (simplest) or resource by resource (generally
not recommended).
Avoid making breaking changes to your API as much as possible. No versioning option is
without its problems.
Securing Web APIs
Use HTTPS
(seriously, just use it)
Windows Auth
Simplest
Well-known
Steps
◦ Authenticate user and issue token. Store in client (local storage for browser).
◦ Add token in header on subsequent requests
◦ Validate token on server using middleware; return 401 if not valid
JWT Demo
Resources
Online Courses (Pluralsight and DevIQ)
• SOLID Principles of OO Design http://bit.ly/SOLID-OOP
• N-Tier Architecture in C# http://bit.ly/PS-NTier1 and http://bit.ly/PS-NTier2
• DDD Fundamentals http://bit.ly/ddd-fundamentals
• ASP.NET Core Quick Start http://aspnetcorequickstart.com/ DEVINTFALL17 20% OFF!
Other Resources
• Weekly Dev Tips Podcast http://www.weeklydevtips.com/
• Microsoft Architecture eBook/sample http://aka.ms/WebAppArchitecture
• Securing Web API in ASP.NET Core
• http://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/