Asp.Net Web Api Http Verbs And Restful Design Principles Complete Guide
Understanding the Core Concepts of ASP.NET Web API HTTP Verbs and RESTful Design Principles
ASP.NET Web API HTTP Verbs and RESTful Design Principles
HTTP Verbs
HTTP verbs, or methods, describe the action to be performed on a given resource. The most commonly used HTTP verbs are GET, POST, PUT, DELETE, and PATCH. Each verb serves a specific purpose, and using them correctly is key to building a robust API.
GET: Intended to retrieve data from the server. GET requests are idempotent and have no side effects on the server.
- Example Use Case: Fetch a list of users.
- Importance: Ensures data can be requested safely and repeatedly with predictable results.
POST: Used to create a new resource on the server. POST requests are not idempotent, meaning making a POST request multiple times can lead to different results.
- Example Use Case: Submit a new blog post.
- Importance: Facilitates data submission and creation, enabling dynamic interaction with server-side resources.
PUT: Designed to update or replace an existing resource on the server. PUT requests are idempotent, so repeated requests do not change the outcome beyond the initial application.
- Example Use Case: Update user profile details.
- Importance: Allows modification or replacement of resources efficiently and safely.
DELETE: Intended to remove a resource from the server. Like PUT, DELETE requests are idempotent.
- Example Use Case: Delete a specific blog post.
- Importance: Provides a means to cleanly and safely remove resources.
PATCH: Used to apply partial modifications to a resource. Unlike PUT, PATCH requests do not replace the entire resource but instead make selective changes.
- Example Use Case: Update only the user's email without changing other information.
- Importance: Enables flexible and efficient resource updates.
RESTful Design Principles
Representational State Transfer (REST) is an architectural style that emphasizes the use of simple, stateless interactions between clients and servers. RESTful principles include:
Uniform Interface: A cornerstone of REST, this principle enforces a uniform interface between clients and servers. This involves the use of HTTP methods, resource identifiers, and hypermedia as the engine of application state (HATEOAS).
- Importance: Simplifies the design of APIs, ensuring consistency and reducing the complexity of client-server interactions.
Idempotency: Ensures that repeated requests to the same endpoint yield the same results after the first request. This is aligned with the use of GET, PUT, and DELETE methods.
- Importance: Builds reliability into APIs, safeguarding against unintended changes due to repeated operations.
Statelessness: Each HTTP request should be independent of previous ones. Servers do not store any session data; clients manage the application state.
- Importance: Improves scalability and resilience, as each request can be handled by any server in a distributed system without reliance on shared state.
Cacheability: Responses should be explicitly marked as cacheable or non-cacheable to optimize performance and reduce load on the server.
- Importance: Enhances user experience by reducing latency and network traffic through caching mechanisms.
Layered System: ASP.NET Web API can be part of a layered architecture, allowing for the separation of concerns within the system. Each layer interacts with a limited view of the system.
- Importance: Enables modular design, improving maintainability and scalability by decoupling components.
Code on Demand (optional): Servers may provide executable code or scripts to be executed on the client, enhancing client capabilities.
- Importance: Extends client functionality, allowing for dynamic behavior based on server-side logic.
Conclusion
Online Code run
Step-by-Step Guide: How to Implement ASP.NET Web API HTTP Verbs and RESTful Design Principles
Introduction
REST (Representational State Transfer) is an architectural design principle that aims to leverage the existing technology and protocols of the web. RESTful APIs use standard HTTP verbs to perform actions on resources. In ASP.NET Web API, we can create these endpoints by defining methods in our controllers corresponding to the HTTP verbs.
Common HTTP Verbs:
- GET: Retrieve data from the server.
- POST: Send data to the server to create/update a resource.
- PUT: Update an existing resource or create it if not exists.
- DELETE: Delete a specific resource.
- PATCH: Partially update a resource.
- OPTIONS: Get information on what HTTP methods are supported by a particular URL.
Step-by-Step Example: Creating a Simple CRUD RESTful API with ASP.NET Core Web API
1. Setting Up Your Project
First, you need to set up a new ASP.NET Core Web API project. You can do this via Visual Studio or using the .NET CLI:
Using Visual Studio:
- Open Visual Studio.
- Create a new project -> ASP.NET Core Web Application.
- Select the Web API template and configure your project details.
Using .NET CLI:
dotnet new webapi -n TodoApi
cd TodoApi
2. Add Model Class
Create a TodoItem
class which will represent each item in your TODO list. This class will be used as the resource for your API.
TodoItem.cs:
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
3. Create a In-Memory Database (for simplicity)
For the sake of this example, we'll use an in-memory database, but in a real-world scenario, you'd connect this to a persistent storage system like SQL Server or MongoDB.
TodoContext.cs:
using Microsoft.EntityFrameworkCore;
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; }
}
4. Configure Services
Go to Startup.cs
or Program.cs
(if using .NET 6+) and register your DbContext
.
Program.cs:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// Register the in-memory database context
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoListDb"));
var app = builder.Build();
app.UseAuthorization();
app.MapControllers();
app.Run();
Alternatively, in Startup.cs of earlier versions (e.g., .NET 5):
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Register the in-memory database context
services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoListDb"));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
5. Create Controller
Now, let’s create a controller to handle requests and return responses. We'll name it TodoItemsController
.
TodoItemsController.cs:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
return await _context.TodoItems.ToListAsync();
}
// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
}
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
// PATCH: api/TodoItems/5
[HttpPatch("{id}")]
public async Task<IActionResult> PatchTodoItem(long id, JsonPatchDocument<TodoItem> patchDoc)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
patchDoc.ApplyTo(todoItem, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
await _context.SaveChangesAsync();
return NoContent();
}
}
6. Test the API
Now that our API is complete, we can test it using tools like Postman or directly via browser.
a) GET
URL: /api/todoitems
Method: GET
Response:
[
{
"id": 1,
"name": "Buy groceries",
"isComplete": false
},
{
"id": 2,
"name": "Do laundry",
"isComplete": true
}
]
b) GET by ID
URL: /api/todoitems/1
Method: GET
Response:
{
"id": 1,
"name": "Buy groceries",
"isComplete": false
}
c) POST
URL: /api/todoitems
Method: POST
Body:
{
"name": "Walk dog",
"isComplete": false
}
Response:
{
"id": 3,
"name": "Walk dog",
"isComplete": false
}
d) PUT
URL: /api/todoitems/3
Method: PUT
Body:
{
"id": 3,
"name": "Walk dog",
"isComplete": true
}
Response: (204 No Content)
e) PATCH
URL: /api/todoitems/3
Method: PATCH
Body:
[
{
"op": "replace",
"path": "/name",
"value": "Feed the parrot"
}
]
Response: (204 No Content)
f) DELETE
URL: /api/todoitems/3
Method: DELETE
Response: (204 No Content)
Explanation of Design Principles
Uniform Interface: Resources identified by URIs (URLs); actions on resources through HTTP methods (verbs).
[Route("api/[controller]")] [ApiController]
Stateless: Each request from client to server must contain all the information needed to understand and complete the request.
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems() { return await _context.TodoItems.ToListAsync(); }
Client-Server Architecture: Front-end and back-end are separated, allowing them to evolve independently. (Handled by default in ASP.NET Core Web API structure)
Cacheable: Responses must be explicitly defined as cacheable or non-cacheable. (Managed by HTTP headers and caching mechanisms)
Layered System: Communication through standardized interfaces allows for intermediary layers such as proxies and gateways. (Handled by default in ASP.NET Core)
Code on Demand: Optionally, extend functionality by transferring executable code from the server to the client. (Not shown in example, optional feature)
Login to post a comment.