mirror of https://github.com/abpframework/abp.git
7 changed files with 190 additions and 3 deletions
@ -0,0 +1,42 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Aspects; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Validation |
|||
{ |
|||
public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency |
|||
{ |
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
public AbpValidationActionFilter(IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) |
|||
{ |
|||
//TODO: Configuration to disable validation for controllers..?
|
|||
if (!context.ActionDescriptor.IsControllerAction() || |
|||
!ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType)) |
|||
{ |
|||
await next(); |
|||
return; |
|||
} |
|||
|
|||
using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.Validation)) |
|||
{ |
|||
using (var scope = _serviceProvider.CreateScope()) |
|||
{ |
|||
var validator = scope.ServiceProvider.GetRequiredService<MvcActionInvocationValidator>(); |
|||
validator.Initialize(context); |
|||
validator.Validate(); |
|||
} |
|||
|
|||
await next(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Microsoft.AspNetCore.Mvc.Filters; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Validation; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Validation |
|||
{ |
|||
public class MvcActionInvocationValidator : MethodInvocationValidator |
|||
{ |
|||
protected ActionExecutingContext ActionContext { get; private set; } |
|||
|
|||
private bool _isValidatedBefore; |
|||
|
|||
public MvcActionInvocationValidator(IOptions<AbpValidationOptions> options) |
|||
: base(options) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public void Initialize(ActionExecutingContext actionContext) |
|||
{ |
|||
ActionContext = actionContext; |
|||
|
|||
SetDataAnnotationAttributeErrors(); |
|||
|
|||
base.Initialize( |
|||
actionContext.ActionDescriptor.GetMethodInfo(), |
|||
GetParameterValues(actionContext) |
|||
); |
|||
} |
|||
|
|||
protected override void SetDataAnnotationAttributeErrors(object validatingObject) |
|||
{ |
|||
SetDataAnnotationAttributeErrors(); |
|||
} |
|||
|
|||
protected virtual void SetDataAnnotationAttributeErrors() |
|||
{ |
|||
if (_isValidatedBefore || ActionContext.ModelState.IsValid) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var state in ActionContext.ModelState) |
|||
{ |
|||
foreach (var error in state.Value.Errors) |
|||
{ |
|||
ValidationErrors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key })); |
|||
} |
|||
} |
|||
|
|||
_isValidatedBefore = true; |
|||
} |
|||
|
|||
protected virtual object[] GetParameterValues(ActionExecutingContext actionContext) |
|||
{ |
|||
var methodInfo = actionContext.ActionDescriptor.GetMethodInfo(); |
|||
|
|||
var parameters = methodInfo.GetParameters(); |
|||
var parameterValues = new object[parameters.Length]; |
|||
|
|||
for (var i = 0; i < parameters.Length; i++) |
|||
{ |
|||
parameterValues[i] = actionContext.ActionArguments.GetOrDefault(parameters[i].Name); |
|||
} |
|||
|
|||
return parameterValues; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Shouldly; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace Volo.Abp.AspNetCore.App |
|||
{ |
|||
[Route("api/validation-test")] |
|||
public class ValidationTestController : AbpController |
|||
{ |
|||
[HttpGet] |
|||
[Route("object-result-action")] |
|||
public Task<string> ObjectResultAction(ValidationTest1Model model) |
|||
{ |
|||
ModelState.IsValid.ShouldBeTrue(); //AbpValidationFilter throws exception otherwise
|
|||
return Task.FromResult(model.Value1); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("action-result-action")] |
|||
public IActionResult ActionResultAction(ValidationTest1Model model) |
|||
{ |
|||
return Content("ModelState.IsValid: " + ModelState.IsValid.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
public class ValidationTest1Model |
|||
{ |
|||
[Required] |
|||
[MinLength(2)] |
|||
public string Value1 { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System.Net; |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.Http; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.Validation |
|||
{ |
|||
public class ValidationTestController_Tests : AspNetCoreMvcTestBase |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Validate_Object_Result_Success() |
|||
{ |
|||
var result = await GetResponseAsStringAsync("/api/validation-test/object-result-action?value1=hello"); |
|||
result.ShouldBe("hello"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Validate_Object_Result_Failing() |
|||
{ |
|||
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/validation-test/object-result-action?value1=a", HttpStatusCode.BadRequest); //value1 has min length of 2 chars.
|
|||
result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Not_Validate_Action_Result_Success() |
|||
{ |
|||
var result = await GetResponseAsStringAsync("/api/validation-test/action-result-action?value1=hello"); |
|||
result.ShouldBe("ModelState.IsValid: true"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Not_Validate_Action_Result_Failing() |
|||
{ |
|||
var result = await GetResponseAsStringAsync("/api/validation-test/action-result-action"); //Missed the value1
|
|||
result.ShouldBe("ModelState.IsValid: false"); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue