Ich mag die obige Antwort von @Tseng, dachte aber darüber nach, noch eine Antwort zu geben, da seine Antwort weitere Szenarien (wie Generika) abdeckt und für einige Benutzer überwältigend sein könnte.
Hier habe ich ein Aktionsfilter-Attribut, das nur die ModelState
überprüft und die Anforderung kurzschließt (gibt die Antwort zurück, ohne dass die Aktion aufgerufen wird), indem die Eigenschaft Result
für den Kontext festgelegt wird. Innerhalb des Filters, versuche ich das Servicelocator Muster zu verwenden, um einen Logger zu bekommen einige Daten einzuloggen (einige vielleicht nicht mögen, aber dies ist ein Beispiel)
Filter
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ValidationFilterAttribute>>();
logger.LogWarning("some message here");
context.Result = new JsonResult(new InvalidData() { Message = "some messgae here" })
{
StatusCode = 400
};
}
}
}
public class InvalidData
{
public string Message { get; set; }
}
Einheit Test
[Fact]
public void ValidationFilterAttributeTest_ModelStateErrors_ResultInBadRequestResult()
{
// Arrange
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(ILogger<ValidationFilterAttribute>)))
.Returns(Mock.Of<ILogger<ValidationFilterAttribute>>());
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProviderMock.Object;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var actionExecutingContext = new ActionExecutingContext(
actionContext,
filters: new List<IFilterMetadata>(), // for majority of scenarios you need not worry about populating this parameter
actionArguments: new Dictionary<string, object>(), // if the filter uses this data, add some data to this dictionary
controller: null); // since the filter being tested here does not use the data from this parameter, just provide null
var validationFilter = new ValidationFilterAttribute();
// Act
// Add an erorr into model state on purpose to make it invalid
actionContext.ModelState.AddModelError("Age", "Age cannot be below 18 years.");
validationFilter.OnActionExecuting(actionExecutingContext);
// Assert
var jsonResult = Assert.IsType<JsonResult>(actionExecutingContext.Result);
Assert.Equal(400, jsonResult.StatusCode);
var invalidData = Assert.IsType<InvalidData>(jsonResult.Value);
Assert.Equal("some messgae here", invalidData.Message);
}
zu instanziieren läuft darauf hinaus, 'ActionExecutingContext' selbst, ein' ActionContext' mit einem verspottet 'HttpContext' (es ist abstrakt, so können Sie es verspotten) vorbei und von hier aus, kehren ein verspottet' IServi ceProvider 'aus' RequestServices'. Genaue Verwendung und Beispiele hängen von Ihrem Mock Framework ab – Tseng