< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.WebApi.Binding.MalformedRequestFilter
Assembly: ArturRios.Heimdall.WebApi
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Presentation/ArturRios.Heimdall.WebApi/Binding/MalformedRequestFilter.cs
Line coverage
100%
Covered lines: 8
Uncovered lines: 0
Coverable lines: 8
Total lines: 52
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 2
Fully covered methods: 2
Total methods: 2
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
OnActionExecuting(...)100%22100%
OnActionExecuted(...)100%11100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Presentation/ArturRios.Heimdall.WebApi/Binding/MalformedRequestFilter.cs

#LineLine coverage
 1using ArturRios.Heimdall.Shared.Messages;
 2using ArturRios.Output;
 3using ArturRios.Util.WebApi.AspNetCore;
 4using Microsoft.AspNetCore.Mvc.Filters;
 5
 6namespace ArturRios.Heimdall.WebApi.Binding;
 7
 8/// <summary>
 9///     Answers <c>400 Bad Request</c> with <see cref="RequestMessages.Malformed" /> when the request
 10///     could not be bound to the action's parameters — a body that is not JSON, an empty one the
 11///     action requires, or a value of the wrong type in the body or query string (NFR-10,
 12///     issue #186).
 13/// </summary>
 14/// <remarks>
 15///     <para>
 16///         The controllers are not <c>[ApiController]</c>, so nothing refuses a failed binding: the
 17///         action ran anyway with a null or half-filled argument, which surfaced as a 500 (a body that
 18///         is not JSON), or as a request executed with a value the caller never sent (a query filter
 19///         of the wrong type silently dropped, answering 200 unfiltered). Adding the attribute would
 20///         change every action's other conventions at once; this filter states only the one rule.
 21///     </para>
 22///     <para>
 23///         An action filter, because that is the stage that runs after model binding (which fills
 24///         <c>ModelState</c>) and after every authorization filter. An authorization filter would run
 25///         before binding and see nothing; and running after them keeps an unauthenticated or
 26///         forbidden caller answering 401 or 403 rather than learning anything about the shape of an
 27///         endpoint's input. Global, so no endpoint can be added that forgets it.
 28///     </para>
 29///     <para>
 30///         The response carries no binding detail: the framework's own messages name properties and
 31///         types of the internal model, which the caller has no use for.
 32///     </para>
 33/// </remarks>
 34public class MalformedRequestFilter : IActionFilter
 35{
 36    public void OnActionExecuting(ActionExecutingContext context)
 37    {
 122038        if (context.ModelState.IsValid)
 39        {
 120840            return;
 41        }
 42
 1243        context.Result = DataOutput<object?>.New
 1244            .WithError(RequestMessages.Malformed)
 1245            .ToActionResult(statusMap: RequestMessageMap.StatusCodes)
 1246            .Result;
 1247    }
 48
 49    public void OnActionExecuted(ActionExecutedContext context)
 50    {
 120851    }
 52}