Basic Usage
AspNetConventions is designed to be zero-effort implementation. This guide explains the core workflow from initialization to final production build, ensuring your application enforces consistent API conventions across your entire ASP.NET Core ecosystem.
MVC Controllers
When using MVC Controllers, the library integrates .AddAspNetConventions() with the IMvcBuilder to inject global action filters and route token transformers. This ensures that every controller and action follows your specified naming conventions without requiring manual [Route] overrides on every method.
Your Code
[ApiController]
[Route("api/[controller]")]
public class ProfileController : ControllerBase
{
[HttpGet("[action]/{UserId:int}")]
public ActionResult GetUser(int UserId) => Ok(new { id = UserId });
[HttpPut("CreateAccount")]
public ActionResult CreateUser([FromBody] CreateAccountRequest request)
{
// Use ApiResults class to return a "201 Created" status code with custom message.
return ApiResults.Created(request, "User created successfully.");
}
}
See ApiResults for more information about custom standardized responses.
Route Transformation
The following table demonstrates how the library automatically transforms standard PascalCase names into clean, SEO-friendly paths.
| Original | Standardized |
|---|---|
GET /api/Profile/GetUser/{UserId:int} |
GET /api/profile/get-user/{user-id:int} |
POST /api/Profile/CreateAccount |
POST /api/profile/create-account |
Customizing the Route Style
If you prefer a different naming convention, such as snake_case or remove prefixes/suffixes on controller/action names, you can define it globally during service registration.
builder.Services
.AddControllers()
.AddAspNetConventions(options =>
{
// Result: GET /api/profile/user/{user_id:int}
options.Route.CaseStyle = CasingStyle.SnakeCase;
options.Route.Controllers.RemoveActionPrefixes.Add("Get");
});
See RouteConventionOptions and ControllerRouteOptions for more information about MVC Controllers route customization.
Minimal APIs
For Minimal APIs, the library provides a .UseAspNetConventions() extension for WebApplication that returns a RouteGroupBuilder. Map your endpoints on the returned group to apply conventions to a specific branch of your API tree or the entire application.
Your Code
var api = app.UseAspNetConventions();
api.MapGet("/WeatherForecast/{city}", (string city) =>
Results.Ok(new { city }));
api.MapGet("/GetUserById/{userId}", (int userId) =>
Results.Ok(new { id = userId }));
Route Transformation
In Minimal APIs, route segments are transformed, but parameters are kept stable by default to ensure binding remains intact.
| Original | Standardized |
|---|---|
GET /WeatherForecast/{city} |
GET /weather-forecast/{city} |
GET /GetUserById/{userId} |
GET /get-user-by-id/{userId} |
Minimal APIs handle parameter binding strictly by name. If the library automatically transforms a route parameter like {userId} to {user-id}, the underlying .NET binder will fail to locate the value unless explicitly told where to look.
Enabling Parameter Transformation
To enable automatic casing for Minimal API route parameters, toggle this feature in your configuration:
app.UseAspNetConventions(options => {
// Default is "false" to prevent binding breaks
options.Route.MinimalApi.TransformRouteParameters = true;
});
The Solution: Explicit Binding
When transformation is enabled, you must use the [FromRoute(Name = "...")] attribute to manually map the transformed segment name back to your C# parameter:
// The URL becomes: /user-account/{user-id}
api.MapGet("/UserAccount/{userId}", ([FromRoute(Name = "user-id")] int userId) => {
// Parameter binding works
return Results.Ok(new { userId });
});
See MinimalApiRouteOptions for more information about Minimal APIs route customization.
Razor Pages
Razor Pages often suffer from inconsistent URL structures due to folder nesting. AspNetConventions automatically transforms physical file paths and route parameters into a consistent casing style, making your UI routes match your API routes.
Your Code
The transformation applies to the page path and any parameters defined in OnGet / OnPost method signatures or @page directives. Additionally, it can transform properties marked with [BindProperty].
// Pages/UserProfile/EditAddress.cshtml.cs
public class EditAddressModel : PageModel
{
[BindProperty(SupportsGet = true)]
public string? ZipCode { get; set; }
public void OnGet(int UserId, int AddressId) { }
}
Route Transformation
| State | Route Example |
|---|---|
| Original | GET /UserProfile/EditAddress/{UserId}/{AddressId}?ZipCode=... |
| Standardized | GET /user-profile/edit-address/{user-id}/{address-id}?zip-code=... |
Disabling Property Transformation
If you need to keep property names in their original C# casing (e.g., for compatibility with external integrations), you can disable property-level transformation.
builder.Services
.AddRazorPages()
.AddAspNetConventions(e =>
e.Route.RazorPages.TransformPropertyNames = false
);
See RazorPagesRouteOptions for more information about razor page route customization.
Response Standardization Examples
A core feature of the library is the Uniform Response Envelope. Whether an endpoint succeeds or fails, the client receives a predictable JSON structure containing the data, status information, and useful debugging metadata.
Success Response
Successful requests are wrapped in a success status with a 2xx code and a metadata object for request tracking.
{
"status": "success",
"statusCode": 201,
"message": "User created successfully.",
"data": {
"userId": 1,
"name": "John Doe"
},
"metadata": {
"requestType": "PUT",
"timestamp": "0000-00-00T00:00:00.000000Z",
"traceId": "00-ed89d1cc507c35126d6f0e933984f774-99b8b9a3feb75652-00",
"path": "/api/users/{id}"
}
}
Validation Error Response
Errors are categorized by type (e.g., VALIDATION_ERROR) and include a detailed errors array that maps specific fields to their failure reasons.
{
"status": "failure",
"statusCode": 400,
"type": "VALIDATION_ERROR",
"message": "One or more validation errors occurred.",
"errors": [
{
"Email": ["'Email' is not a valid email address."]
}
],
"metadata": {
"requestType": "PUT",
"timestamp": "0000-00-00T00:00:00.000000Z",
"traceId": "00-8e5513ae9369648487c2323d9a3508aa-2a8f92c7d45d3f74-00",
"path": "/api/users/{id}"
}
}
Global Response Configuration
You can control the verbosity of your responses globally, such as hiding metadata in production or changing default messages.
builder.Services.AddControllers()
.AddAspNetConventions(options =>
{
// Change Json serialization to "snake_case"
options.Json.CaseStyle = CasingStyle.SnakeCase;
// Hide metadata for cleaner responses
options.Response.IncludeMetadata = false;
options.Response.Pagination.DefaultPageSize = 50;
options.Response.ErrorResponse.DefaultValidationMessage = "Check your input.";
});
See ResponseFormattingOptions and JsonSerializationOptions for more information about response and json serialization customization.
Next Steps
Explore the deep-dive documentation for each specific feature to customize the behavior to your needs: