Understanding REST: Representational State Transfer
Fundamental introduction to REST architectural principles, constraints, and evolution
4 minute read
This comprehensive documentation serves as your complete guide to understanding, designing, implementing, and consuming REST APIs in the .NET ecosystem. Whether you’re building your first API or looking to refine your existing services, this resource provides practical guidance, best practices, and real-world examples.
This documentation is regularly updated with the latest .NET REST practices and patterns as of May 2025.
This documentation is designed for:
The documentation is structured to provide a complete learning path from fundamental REST principles to advanced implementation techniques:
Section | What You’ll Find |
---|---|
What is REST? | Core REST concepts, architectural constraints, and the historical evolution from SOAP to modern REST |
Why REST? | Strategic benefits, ideal use cases, limitations, and comparisons with alternative approaches |
Basics | Resource modeling, RESTful constraints, content negotiation, and essential HTTP concepts |
Best Practices | API design patterns, status code usage, versioning strategies, and security implementation |
Libraries | In-depth analysis of .NET client libraries (HttpClient, Refit, RestSharp) and server frameworks |
Alternatives | Comparative analysis of gRPC, GraphQL, SOAP, and event-driven architectures for specialized scenarios |
New to REST APIs? We recommend starting with these foundational articles:
Throughout this documentation, you’ll find practical code samples that demonstrate REST concepts. Most examples use C# with ASP.NET Core for server implementations and various client libraries for consumption.
// Simple ASP.NET Core REST endpoint
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<Product>), StatusCodes.Status200OK)]
public ActionResult<IEnumerable<Product>> GetAll()
{
IEnumerable<Product> products = _productService.GetAllProducts();
return Ok(products);
}
[HttpGet("{id}")]
[ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
Product product = _productService.GetById(id);
if (product == null)
return NotFound();
return Ok(product);
}
[HttpPost]
[ProducesResponseType(typeof(Product), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Product> Create(ProductCreateDto productDto)
{
Product newProduct = _productService.CreateProduct(productDto);
return CreatedAtAction(
nameof(GetById),
new { id = newProduct.Id },
newProduct
);
}
}
Running examples are available in the samples directory of the repository.
When designing REST APIs in the .NET ecosystem, adhering to these core principles will ensure your APIs are intuitive, maintainable, and aligned with industry standards:
Structure your API around resources (nouns) rather than actions (verbs):
✅ Good Resource Design:
GET /api/products
- Retrieve all productsGET /api/products/123
- Retrieve product with ID 123POST /api/products
- Create a new productPUT /api/products/123
- Update product with ID 123❌ Poor Resource Design:
GET /api/getProducts
- Using verbs in URLsPOST /api/createProduct
- Action-oriented instead of resource-orientedGET /api/productData/123
- Inconsistent resource namingEach HTTP method carries specific semantics in a RESTful design:
Method | Purpose | Idempotent | Safe |
---|---|---|---|
GET | Read resources | Yes | Yes |
POST | Create resources | No | No |
PUT | Replace resources | Yes | No |
PATCH | Partially update resources | No* | No |
DELETE | Remove resources | Yes | No |
*PATCH can be idempotent depending on implementation
Communicate results clearly with appropriate HTTP status codes:
// Example of proper status code usage
[HttpPost]
public ActionResult<Order> CreateOrder(OrderCreateDto orderDto)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); // 400 Bad Request
if (orderDto.CustomerId <= 0)
return UnprocessableEntity(new { Error = "Invalid customer ID" }); // 422 Unprocessable Entity
try
{
Order newOrder = _orderService.CreateOrder(orderDto);
return CreatedAtAction(nameof(GetOrder), new { id = newOrder.Id }, newOrder); // 201 Created
}
catch (InsufficientInventoryException ex)
{
return Conflict(new { Error = ex.Message }); // 409 Conflict
}
}
Rely on ASP.NET Core’s built-in dependency injection container to decouple components:
// Service registration in Program.cs
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IProductService, ProductService>();
Keep controllers focused on HTTP concerns while delegating business logic to services:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService;
private readonly ILogger<OrdersController> _logger;
public OrdersController(IOrderService orderService, ILogger<OrdersController> logger)
{
_orderService = orderService;
_logger = logger;
}
[HttpGet("{id}")]
public async Task<ActionResult<OrderDto>> GetOrder(int id)
{
_logger.LogInformation("Retrieving order {OrderId}", id);
OrderDto order = await _orderService.GetOrderByIdAsync(id);
if (order == null)
{
_logger.LogWarning("Order {OrderId} not found", id);
return NotFound();
}
return Ok(order);
}
}
Continue exploring the documentation to build your expertise in REST API development:
Fundamental introduction to REST architectural principles, constraints, and evolution
Explore the benefits, use cases, and strategic advantages of REST API architecture