ASP.NET Web API (.NET Framework) · REST Foundations

REST Principles

REST is a set of rules that makes APIs predictable. If you know one well-designed REST API, you can guess how the next one works — that is the whole point.

Beginner 6 min Lesson 2 of 36Covers ASP.NET Web API 2.2 on .NET Framework 4.8.
Before you start:Introduction to Web APIs
0/36 in ASP.NET Web API
On this page · 6 min · Beginner

Learning objectives#

  • Explain resources, URIs and representations
  • Design URLs with nouns, not verbs
  • Use HTTP methods for actions
  • Explain why REST APIs are stateless

Explanation#

A resource is a thing your API exposes: a customer, an order, a product. Each has a URI: /api/customers/5.

URLs are nouns, usually plural. The action comes from the HTTP method: GET reads, POST creates, PUT replaces, PATCH updates part, DELETE removes. /api/getCustomer?id=5 is RPC style, not REST.

Relationships are expressed by nesting: /api/customers/5/orders is “the orders of customer 5”. Keep nesting to one level.

Stateless: every request carries everything the server needs (including the auth token). The server keeps no session between requests, so any server in a farm can answer — which is how APIs scale.

A representation is the format of the data, usually JSON. Clients ask with the Accept header; Web API 2 can also return XML unless you remove that formatter.

Use status codes to report outcomes (201 Created, 404 Not Found) instead of returning 200 with an error message inside.

Syntax#

Text Illustrative
✓ GET    /api/customers            list customers
✓ GET    /api/customers/5          one customer
✓ POST   /api/customers            create a customer
✓ GET    /api/customers/5/orders   orders of customer 5
✗ GET    /api/getAllCustomers
✗ POST   /api/deleteCustomer?id=5

Code example: A RESTful customers controller#

C#CustomersController.cs Illustrative
using System.Web.Http;


public class CustomerDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}


[RoutePrefix("api/customers")]
public class CustomersController : ApiController
{
    // GET api/customers
    [HttpGet, Route("")]
    public IHttpActionResult GetAll()
    {
        return Ok(new[] { new CustomerDto { Id = 1, Name = "Asha" }, new CustomerDto { Id = 2, Name = "Ravi" } });
    }


    // GET api/customers/1
    [HttpGet, Route("{id:int}")]
    public IHttpActionResult GetById(int id)
    {
        return Ok(new CustomerDto { Id = id, Name = "Asha" });
    }


    // GET api/customers/1/orders   — a related resource
    [HttpGet, Route("{id:int}/orders")]
    public IHttpActionResult GetOrders(int id)
    {
        return Ok(new[] { new { OrderId = 101, CustomerId = id, Total = 1499.50m } });
    }


    // POST api/customers   — create; reply 201 with the new resource's URL
    [HttpPost, Route("")]
    public IHttpActionResult Create(CustomerDto customer)
    {
        customer.Id = 3;
        return Created("api/customers/" + customer.Id, customer);
    }
}
Expected output
GET  /api/customers           → 200 [{"Id":1,"Name":"Asha"},{"Id":2,"Name":"Ravi"}]
GET  /api/customers/1/orders  → 200 [{"OrderId":101,"CustomerId":1,"Total":1499.50}]
POST /api/customers {"Name":"Meera"}
                              → 201 Created, Location: api/customers/3, body {"Id":3,"Name":"Meera"}

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. [RoutePrefix("api/customers")] names the resource collection once for the whole controller.
  2. GET on the collection lists customers; GET with an id returns one.
  3. {id:int}/orders exposes a related resource under its parent.
  4. POST on the collection creates a customer and answers 201 Created with a Location header pointing to it.
  5. No URL contains a verb — the HTTP method says what to do.

Real-world use case#

A bank’s partner API follows REST strictly, so partners integrate in days: once they have used GET /api/accounts/{id}, they correctly guess GET /api/accounts/{id}/transactions without reading the docs.

Common mistakes#

Verbs in URLs

/api/products/delete/5 duplicates what DELETE /api/products/5 already says.

200 OK with an error body

Returning 200 { "success": false } forces every client to parse the body to detect failure. Use 4xx/5xx codes.

Server-side session state

Storing the user’s cart in Session breaks when a load balancer sends the next request to another server. Keep APIs stateless.

Best practices#

  • Plural nouns for collections (/api/products)
  • One level of nesting at most
  • Let HTTP methods carry the action
  • Return meaningful status codes and Location headers

Remember this#

URLs are nouns; HTTP methods are verbs.

Stateless: each request carries everything it needs.

Resource = thing. Representation = its JSON.

Practice exercise#

Your task: Design REST endpoints (method + URL) for a blog: list posts, read one post, create a post, update a post, delete a post, list comments of a post, add a comment to a post.

Need a hint?
Two resources: posts and comments (comments nested under a post).
Show solution
Text Illustrative
GET    /api/posts
GET    /api/posts/{id}
POST   /api/posts
PUT    /api/posts/{id}
DELETE /api/posts/{id}
GET    /api/posts/{id}/comments
POST   /api/posts/{id}/comments

Mini quiz#

REST Principles quizMultiple choice Question 1 of 3

Which URL follows REST conventions to delete product 7?

Score so far: 0/0

Want more? Practise all ASP.NET Web API MCQs · ASP.NET Web API interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.