cswork / libraryOpen practice workspace

Articles

ASP.NET Core MVC Interview Questions: Model Binding, Validation, and Filters

Prepare for ASP.NET Core MVC interviews with request-source traces, binding and validation failures, ModelState checks, filters, and clear save boundaries.

An ASP.NET Core MVC action can receive a quantity of zero because the caller sent zero, because a value was missing, or because conversion failed. Those cases can lead to different errors even when the model property looks the same, which changes the feedback you should return. A useful interview answer follows the request source, conversion result, validation result, and decision to save.

This guide uses Microsoft's ASP.NET Core 10 documentation and original request fixtures. It distinguishes MVC controllers with views from [ApiController] APIs; it does not describe a reported employer interview format.

How should you trace an MVC request?

Start by naming the controller style, route, HTTP method, content type, action signature, and binding attributes. These inputs determine the contract. A discussion that begins with “route values always win” can give the wrong value before it reaches validation.

Microsoft's model-binding documentation describes reading request values, converting them into .NET types, and recording binding information in ModelState. It also distinguishes input formatters for body content from key-value model binding.

Our answer sequence is source, attempted value, typed value, recorded error, and next action. Keep attempted input visible when conversion fails. Looking only at the resulting property can hide whether zero was submitted or produced as a default after a failed conversion.

State any customized binder, value provider, or API behavior before predicting the result. Defaults are useful baselines, but an application's configuration can change them. Your explanation should name the specific contract it is tracing rather than claiming one outcome for all MVC requests.

Which value wins when sources conflict?

For ordinary default key-value binding, Microsoft documents form values ahead of route and query values, with source-specific exceptions. Explicit [FromRoute], [FromQuery], and [FromForm] attributes make the intended source clearer. JSON is not simply another form field provider.

Consider our original request: route /orders/42, query ?id=99, and a form field id=7. Assume an ordinary controller with views, default providers, and no custom binder. Compare these signatures:

TargetDeclared sourceExpected id under this fixture
int idDefault key-value binding7 from the form
[FromRoute] int idRoute42
[FromQuery] int idQuery99
[FromForm] int idForm7

This is a source-selection exercise, not a recommended API accepting three conflicting IDs. In a real update contract, name the authoritative route identity and decide how a body identity mismatch should be rejected.

Do not add [ApiController] to this fixture without revisiting inference rules. An API controller can infer binding sources under its own documented behavior. Explicit sources make the intended demonstration easier to assess across controller styles.

For a follow-up, ask what happens when id is missing from the explicitly selected source. A value in another source does not automatically override an explicit source declaration. Define whether absence is allowed and how the resulting binding state should be handled.

Why does FromBody change the reasoning?

[FromBody] delegates body reading to an input formatter. Microsoft's reference says property-level source attributes on a complex parameter are ignored when that parameter is read from the body. A property labelled [FromQuery] does not cause the JSON formatter to fetch that property's value from the query string.

Our original example is an API request with JSON containing {"quantity":2} and query ?quantity=9. With a complex body input, the JSON quantity belongs to that body contract. If the endpoint needs an additional query setting, model it as a separate explicitly sourced parameter.

Content type and payload shape remain important. A body containing malformed JSON fails before the desired model is available. An unsupported content type is a formatter-selection problem, not a reason to search route values for replacement JSON fields.

Avoid explaining [Bind] as a universal JSON property whitelist. Microsoft's documentation distinguishes form-binding attributes from input formatter behavior. A small dedicated input DTO makes allowed client fields easier to see, while server-owned fields stay outside the input model.

For an order request, omit approval state and calculated price from the client DTO. Load or calculate those values through the application's authoritative service. A successfully bound client-supplied field is not evidence that the client is entitled to set it.

Distinguish conversion failure from validation failure

Microsoft's validation reference states that ModelState includes errors from both binding and validation. Conversion errors and rule violations are distinct causes. That distinction is the core of this original fixture:

public sealed class OrderInput
{
    [Range(1, 10)]
    public int Quantity { get; set; }
}

The snippet assumes System.ComponentModel.DataAnnotations is imported. With form input Quantity=x, conversion to an integer fails and ModelState becomes invalid. With Quantity=0, integer conversion succeeds, but the declared range fails.

Both requests must avoid saving in our controller contract. They should not necessarily display the same explanation: one asks the user for a number, while the other asks for a number within the allowed range.

An omitted non-nullable integer can leave its default zero under the assumed binding contract, which then fails this range. [Required] on a non-nullable integer is not a general way to distinguish a missing value from a supplied zero. A nullable property plus an explicit required rule can represent absence when that distinction matters.

Do not conflate annotation validation with every business invariant. Quantity 2 may satisfy the range while stock availability or account permissions still reject the order. Those checks belong to the appropriate service and transaction boundary, with a clear user-visible result.

Does invalid ModelState prevent the action from running?

For ordinary MVC controllers with views, the action typically checks ModelState.IsValid and returns the view with errors when invalid. An invalid model does not, by itself, prove that your ordinary action never executed.

For [ApiController], Microsoft's web API documentation describes the default automatic HTTP 400 behavior through the model-state-invalid action filter. The action can be bypassed when invalid model state is handled by that behavior. Configuration can suppress or customize it.

Our original controller-with-view skeleton is deliberately explicit:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([FromForm] OrderInput input)
{
    if (!ModelState.IsValid)
        return View(input);

    if (!orders.CanAccept(input.Quantity))
    {
        ModelState.AddModelError(nameof(input.Quantity),
                                 "This quantity is unavailable.");
        return View(input);
    }

    orders.Create(input.Quantity);
    return RedirectToAction(nameof(Index));
}

Here orders represents an injected service, and Index is the controller's listing action. The example demonstrates control flow, not a compiled or complete application. In production, checking and creating must share a service contract that prevents a concurrent stock change from invalidating the decision.

The anti-forgery attribute addresses a form-request boundary; it does not validate quantities or authorize ownership of a resource. Authentication, authorization, input validation, and business acceptance remain distinct responsibilities.

When redisplaying a view, preserve the attempted input and error messages through the intended MVC model-state behavior. Replacing a property value in code does not always replace what the form helper displays while ModelState still retains the attempted value. Diagnose the relevant entry before clearing state broadly.

Which filters can act before binding?

Microsoft's filter documentation places resource filters before model binding and action filters around action execution after binding. Filters run within MVC action invocation; middleware surrounds a broader request pipeline.

A resource filter can short-circuit before the model is bound. For example, our original cache-hit exercise returns a cached result without invoking the action. An action filter that inspects ActionArguments operates at a later boundary where bound arguments are available.

Use the smallest boundary that observes the information you need. A request-wide correlation concern may belong in middleware. A behavior dependent on the selected action's metadata may fit an MVC filter. A domain invariant belongs in the service even when a filter also checks request shape.

For nested filters, describe entry and exit rather than memorizing a flat list. Outer filters enter before inner filters and unwind afterward when execution continues normally. Filter type, explicit order, and scope all affect the sequence; a controller-level label alone does not settle every ordering case.

Keep the async short-circuit contract clear. An action filter that sets a result and elects not to call the continuation prevents subsequent action execution. Calling the continuation anyway can invalidate the intended shortcut.

Which exception boundary catches a failed result?

The detailed exception-filter section in Microsoft's reference describes handling failures in controller creation, binding, action filters, and actions. It excludes resource filters, result filters, and MVC result execution from that exception-filter boundary. Locate the actual failing stage before selecting an error handler.

Our original trace has two failures. In the first, orders.Create throws during the action. In the second, the action returns a view result, but rendering the view throws later. The fact that both failures belong to one request does not give them identical filter coverage.

Use appropriate exception-handling middleware for a broader request-level policy, with action-specific exception filters only when that narrower behavior is needed. A result filter may observe result execution, but an exception filter should not be described as catching every rendering failure.

Once response content has started, replacing it with a clean error response may no longer be possible. State that boundary in the trace. Do not propose writing an unrelated successful JSON response over a partially emitted HTML body.

Also distinguish an ordinary validation result from an exception. Setting a filter result to return a controlled validation response is deliberate short-circuiting. Throwing an exception changes the error path and the observers that may run.

What should a meaningful MVC test observe?

Our fixture matrix asks for the source, typed value, ModelState error, action execution, and service-call count. It tests the behavior the article promises rather than merely checking that a DTO has an attribute.

For the controller-with-view case, Quantity=x and Quantity=0 should both produce zero create calls, through different error causes. Quantity=2 should reach the service contract, with creation only if the service accepts it. A business rejection should return the intended view with an error.

Test route, query, and form conflicts using HTTP requests through a host. Directly calling the controller method bypasses model binding, so it cannot prove which request source was selected. Likewise, assigning an already invalid object does not simulate a conversion error in the binder.

For the API controller case, test the automatic invalid-state response separately from the ordinary MVC case. Check whether the action and service were invoked, not just the response status. Then test any configured suppression policy explicitly.

A useful filter test records a before-and-after trace and injects a failure at a known stage. One cache-hit case should prove that binding and the action were bypassed. A separate rendering-failure case should prove the chosen error boundary's actual behavior.

These are proposed integration checks. No running ASP.NET Core application or C# compiler execution is claimed in this article; the request expectations were reviewed against the referenced documentation.

Five questions for input and validation reasoning

Adapt each prompt to an explicit MVC controller contract before answering.

QuestionFollow-up to rehearse
Debug Validation Error AggregationKeep attempted inputs and distinct error causes.
Code Review of a Multi-File HTTP API: Wrong Method, Payload and Validation BugsIdentify method, source, conversion and save boundaries.
Implement a robust REST API methodWrite an explicit request and error contract.
Reason About Nested JSON Schema ValidationDistinguish missing, malformed and rule-invalid fields.
Implement a REST GET Endpoint and Evolve Its Contract SafelyPreserve source and validation behavior when changing an API.

Choose one Backend Engineer practice question and draw its request ledger. Your answer should name the source, conversion result, validation result, filter boundary, and condition that permits a service write.

Sources and Further Reading

Research checked October 5, 2026. Original fixtures distinguish documented defaults from application-specific policies; Minimal API and classic ASP.NET MVC behavior are outside this guide's contract.