Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
ASP.NET Core Basics: Pipeline, Services and Settings  ›  Lesson

Filters and Attributes — Hooks Around an Action

Filters and Attributes 15 minFilters run code at set stages around a controller action or endpoint, and attributes attach them, along with routing and validation rules, directly to the code they affect.

Filters and Attributes — Hooks Around an Action

Middleware sees every request but knows nothing about which controller action will run. Some rules need that knowledge: "log the arguments of every borrow action", "reject this action if the book has no copies left". Filters hook in around the action itself, and attributes stick them right where they apply.

The rule

Middleware wraps every request; filters wrap the chosen action.

An attribute is metadata in square brackets on a class, method or parameter. You have already used several: [HttpGet], [Route], [ApiController], [FromBody], and validation rules like [Required] and [Range(1, 10)]. Some attributes attach filters, such as [ServiceFilter] below. [Authorize] is metadata too: the authorization middleware reads it and refuses the request before your action is reached.

MVC and Razor Pages run filters in fixed stages:

  1. Authorization filters — custom "may this caller run it?" checks.
  2. Resource filters — before model binding; good for caching.
  3. (model binding and validation)
  4. Action filters — just before and after the action; can see its arguments.
  5. Exception filters — handle exceptions thrown by the action.
  6. Result filters — around the writing of the result.

Any filter can short-circuit by setting a result, and then the action never runs.

public class CopiesAvailableFilter(LibraryDb db) : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var bookId = (int)context.ActionArguments["bookId"]!;
        if (await db.CopiesAvailableAsync(bookId) == 0)
        {
            context.Result = new ConflictObjectResult("No copies left.");
            return;                     // the action is skipped
        }
        await next();                   // run the action
    }
}

[HttpPost("{bookId:int}/borrow")]
[ServiceFilter(typeof(CopiesAvailableFilter))]
public IActionResult Borrow(int bookId) { /* ... */ }

Register the filter with builder.Services.AddScoped<CopiesAvailableFilter>(); so [ServiceFilter] can build it. Minimal APIs have their own version: endpoint filters, added with .AddEndpointFilter(...).

The shortcut

Ask two questions. Does the rule apply to every request? Use middleware. Does it need the action or its bound arguments? Use a filter — an action filter if it needs the arguments.

Placing three ReadingRoom rulesStep 1 of 3
  1. Log every request's duration

    Every request, no action knowledge needed. Middleware.

The trap: parsing the URL inside middleware to guess which action will run. That is exactly what model binding and filters already do.

Drills

Answer fast — trust the rule.

quick drill

Which filter runs first for an MVC action?

quick drill

A rule needs the action's bound bookId argument. Which filter fits?

quick drill

How does a filter stop the action from running?

quick drill

What does [Range(1, 10)] on a property do in an [ApiController]?

Remember

  • Middleware wraps every request; filters wrap the chosen action.
  • Authorization → resource → binding → action → exception → result.
  • Need the bound arguments? Action filter, attached with an attribute.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainFilter or middleware?

ReadingRoom must reject any borrow request for a book whose CopiesAvailable is zero, but only on the borrowing actions. Explain whether you would use a middleware or a filter, which kind of filter, and how you would attach it.