What is Routing? Routing in ASP.NET Core is the process of mapping an incoming HTTP request to a particular controller action. When a request is made to an API, routing helps to determine which action should handle it based on the URL and other request parameters.
There are two main types of routing:
- Attribute Routing: Defined by placing attributes directly on controllers and actions.
- Conventional Routing: Defined centrally in the
Program.cs
file using a pattern.
1. IActionResult and How to Use It
What is IActionResult
? IActionResult
is an interface in ASP.NET Core that represents a result of an action method in a controller. When you define an action in your Web API controller, you often need to specify how the response will be returned to the client.
IActionResult
provides flexibility, allowing you to return different types of HTTP responses (e.g.,Ok
,BadRequest
,NotFound
, etc.).- It is very useful when you need to return different response types based on conditions (e.g., success vs. failure).
Examples of IActionResult
in Use:
[HttpGet("product/{id}")]
public IActionResult GetProduct(int id)
{
if (id <= 0)
{
return BadRequest("Invalid product ID.");
}
var product = new Product { Id = id, Name = "Sample Product", Price = 10.0m };
if (product == null)
{
return NotFound();
}
return Ok(product);
}
Ok(object value)
: Returns a status code of 200 OK with the specified data.BadRequest(string message)
: Returns a 400 Bad Request response with an error message.NotFound()
: Returns a 404 Not Found response.
Real-Life Example: In a financial application, you might use IActionResult
to return different responses based on whether the transaction was successful (Ok
) or if there was an error (BadRequest
).
2. MapControllerRoute and How to Use It
What is MapControllerRoute
? MapControllerRoute
is used to define conventional routing in ASP.NET Core. It allows you to create routes that can map to your controllers and actions using a URL pattern defined in the Program.cs
file.
Example of Using MapControllerRoute
:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
app.Run();
- Pattern:
"default"
is the name of the route, and"pattern"
is the format of the URL.{controller=Home}
: Indicates the default controller is "Home" if none is specified.{action=Index}
: Indicates the default action is "Index" if none is specified.{id?}
: Theid
parameter is optional, denoted by?
.
When to Use MapControllerRoute
:
- When you need to define a central set of routes that are shared across controllers.
- When your application has a standard URL structure.
3. Attribute Routing vs. Conventional Routing
Attribute Routing:
Routing rules are defined using attributes directly on controller classes and methods.
Example:
[ApiController]
[Route("api/products")]
public class ProductController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
return Ok(new Product { Id = id, Name = "Sample Product", Price = 10.0m });
}
}
[Route("api/products")]
defines the base route for the controller.[HttpGet("{id}")]
defines the route for an individual action, where {id}
is a parameter.
Conventional Routing:
Routing is defined centrally in the Program.cs
file using MapControllerRoute
.
Example:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Conventional routing is easier to manage for small projects where there is a predictable pattern.
Difference:
- Attribute Routing gives you fine-grained control over how each endpoint is exposed and is preferred for building REST APIs since it makes routes more explicit.
- Conventional Routing is better for larger MVC projects where URLs follow a predictable pattern, and you want to configure them centrally.
Real-Life Example: In an e-commerce application:
- Attribute Routing might be used for APIs (
/api/products/{id}
) where each endpoint must be clearly defined. - Conventional Routing might be used for the customer-facing part of the website (
/products/list
or/products/details/{id}
) where there is a common structure.
4. Endpoints: What Are They and How to Use Them
What are Endpoints? Endpoints are units of routing information that tell ASP.NET Core where to send a specific request. Each endpoint represents a controller action or a route handler that handles a specific request.
- An endpoint could be a controller action (
/api/products
), a Razor page, or a custom delegate. - Endpoints are registered using the
MapControllers()
,MapGet()
,MapPost()
, etc., methods.
Example:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Automatically maps all controllers
endpoints.MapGet("/status", async context =>
{
await context.Response.WriteAsync("API is running!");
});
});
MapControllers()
: Maps all controllers with attribute routing to endpoints.MapGet()
: Creates a simple endpoint to respond to a GET request.
When to Use Endpoints:
- Use
MapControllers()
for API controllers. - Use
MapGet()
,MapPost()
, etc., for simple endpoints when you need custom, lightweight handlers (e.g., a health check route).
Real-Life Example:
- In an e-commerce API, endpoints for listing products (
GET /api/products
) or creating new orders (POST /api/orders
) are defined to handle specific functionality. - You may also define an endpoint like
/status
to verify that your API is running properly.
Summary
IActionResult
: Represents different types of HTTP responses and is used to provide flexibility in returning responses from controller actions.MapControllerRoute
: Used for defining conventional routing in a central way in theProgram.cs
file, making it useful for creating common patterns across multiple controllers.- Attribute Routing vs. Conventional Routing:
- Attribute Routing is more explicit and gives better control for API development.
- Conventional Routing is centralized and works well for apps with a common structure.
- Endpoints: Represent routing information and are used to determine which controller action should handle a request. They are registered in
UseEndpoints()
for better control over the routing pipeline.
Project Structure Overview
- Controllers:
ProductController
to handle CRUD operations for products. - Routing: Attribute routing in the controller, and conventional routing set up in
Program.cs
. - Middleware: Middleware is added for logging requests.
- Dependency Injection:
IProductService
is used to manage product operations.
Code for a Full ASP.NET Core Web API Program
Step 1: Create a Product Model
Create a Product
model to represent products in the API.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Step 2: Create a Product Service Interface and Implementation
Define an interface IProductService
and its implementation ProductService
.
public interface IProductService
{
List<Product> GetAllProducts();
Product GetProductById(int id);
void AddProduct(Product product);
}
public class ProductService : IProductService
{
private readonly List<Product> _products = new List<Product>
{
new Product { Id = 1, Name = "Laptop", Price = 1000.00m },
new Product { Id = 2, Name = "Phone", Price = 500.00m }
};
public List<Product> GetAllProducts() => _products;
public Product GetProductById(int id) => _products.FirstOrDefault(p => p.Id == id);
public void AddProduct(Product product) => _products.Add(product);
}
Step 3: Create the Product Controller
The ProductController
will handle HTTP requests for managing products.
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public IActionResult GetProducts()
{
var products = _productService.GetAllProducts();
return Ok(products);
}
[HttpGet("{id}")]
public IActionResult GetProductById(int id)
{
var product = _productService.GetProductById(id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
[HttpPost]
public IActionResult AddProduct([FromBody] Product product)
{
_productService.AddProduct(product);
return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);
}
}
- Attribute Routing: Each action uses attribute routing, such as
[HttpGet("{id}")]
to match/api/product/{id}
. IActionResult
: Used to return different types of HTTP responses (e.g.,Ok()
,NotFound()
,CreatedAtAction()
).
Step 4: Configure Middleware and Routing in Program.cs
Configure dependency injection, middleware, and routing in the entry point of the application.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddScoped<IProductService, ProductService>();
var app = builder.Build();
// Add middleware for logging requests
app.Use(async (context, next) =>
{
Console.WriteLine($"Incoming request: {context.Request.Method} {context.Request.Path}");
await next.Invoke();
});
// Use routing and endpoints
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
// Conventional Routing
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// Attribute Routing
endpoints.MapControllers();
});
// Run the application
app.Run();
Step 5: Example Requests
Here's how different components of the program come together when interacting with the API.
- Request:
GET /api/product
- Handled by:
ProductController.GetProducts()
- Response: Returns a list of all products (
200 OK
).
- Handled by:
- Request:
GET /api/product/1
- Handled by:
ProductController.GetProductById(int id)
- Response: Returns the product with ID 1, or
404 Not Found
if it doesn't exist.
- Handled by:
- Request:
POST /api/product
- Handled by:
ProductController.AddProduct(Product product)
- Body: A JSON object representing the new product.
- Response: Returns
201 Created
with the location of the newly created product.
- Handled by:
Explanation of Key Concepts
- Attribute Routing vs. Conventional Routing:
- Attribute Routing: Used in
ProductController
to specify routes directly on actions. - Conventional Routing: Defined in
Program.cs
usingMapControllerRoute
, which provides a default route for controllers.
- Attribute Routing: Used in
- Middleware:
- Custom middleware in
Program.cs
logs incoming requests. - Middleware is executed in the order it’s added to the pipeline.
- Custom middleware in
- Dependency Injection:
IProductService
is registered in the DI container (builder.Services.AddScoped<IProductService, ProductService>()
).- The
ProductController
receivesIProductService
via constructor injection, which is a best practice to decouple the service logic from the controller.
- Endpoints:
MapControllers()
maps all controllers that use attribute routing.- Conventional routing via
MapControllerRoute()
provides a fallback default route.
Summary
This full example demonstrates how different ASP.NET Core Web API components work together:
- Controllers handle incoming requests.
- Routing (attribute and conventional) helps map these requests to the right actions.
- Middleware processes requests and responses, e.g., logging them.
- Dependency Injection helps keep code modular and testable.