Base solution for your next web application
Open Closed

Exception is thrown before model validation is checked #1704


User avatar
0
djfuzzy created

I have a view model that has some validation attributes:

public class SignUpViewModel
    {
        [Required]
        [StringLength(User.MaxNicknameLength, MinimumLength = User.MinNicknameLength)]
        public string Nickname { get; set; }

        [Required]
        [EmailAddress]
        [StringLength(User.MaxEmailAddressLength)]
        public string EmailAddress { get; set; }

        [StringLength(User.MaxPlainPasswordLength, MinimumLength = User.MinPlainPasswordLength)]
        [DisableAuditing]
        public string Password { get; set; }

        public bool IsExternalLogin { get; set; }
    }

However, if validation fails, before the controller action method is called, which would call the CheckModelState method, an exception is thrown:

Server Error in '/' Application.

Method arguments are not valid! See ValidationErrors for details.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: Abp.Runtime.Validation.AbpValidationException: Method arguments are not valid! See ValidationErrors for details.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[AbpValidationException: Method arguments are not valid! See ValidationErrors for details.] Abp.Runtime.Validation.Interception.MethodInvocationValidator.Validate() in D:\Halil\GitHub\aspnetboilerplate\src\Abp\Runtime\Validation\Interception\MethodInvocationValidator.cs:92 Abp.Web.Mvc.Validation.AbpMvcValidationFilter.OnActionExecuting(ActionExecutingContext filterContext) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.Web.Mvc\Web\Mvc\Validation\AbpMvcValidationFilter.cs:35 System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +176 System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +644 System.Web.Mvc.Async.AsyncInvocationWithFilters.InvokeActionMethodFilterAsynchronouslyRecursive(Int32 filterIndex) +644 System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__31(AsyncCallback asyncCallback, Object asyncState) +58 System.Web.Mvc.Async.WrappedAsyncResult1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeActionMethodWithFilters(ControllerContext controllerContext, IList1 filters, ActionDescriptor actionDescriptor, IDictionary2 parameters, AsyncCallback callback, Object state) +197 System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState) +743 System.Web.Mvc.Async.WrappedAsyncResult1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +343 System.Web.Mvc.Controller.<BeginExecuteCore>b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +25 System.Web.Mvc.Async.WrappedAsyncVoid1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +465 Castle.Proxies.AccountControllerProxy.BeginExecuteCore_callback(AsyncCallback callback, Object state) +8 Castle.Proxies.Invocations.Controller_BeginExecuteCore.InvokeMethodOnTarget() +80 Castle.DynamicProxy.AbstractInvocation.Proceed() +80 Abp.Domain.Uow.UnitOfWorkInterceptor.Intercept(IInvocation invocation) in D:\Halil\GitHub\aspnetboilerplate\src\Abp\Domain\Uow\UnitOfWorkInterceptor.cs:29 Castle.DynamicProxy.AbstractInvocation.Proceed() +108 Castle.Proxies.AccountControllerProxy.BeginExecuteCore(AsyncCallback callback, Object state) +160 System.Web.Mvc.Controller.<BeginExecute>b__14(AsyncCallback asyncCallback, Object callbackState, Controller controller) +18 System.Web.Mvc.Async.WrappedAsyncVoid1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +20 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +374 Castle.Proxies.AccountControllerProxy.BeginExecute_callback(RequestContext requestContext, AsyncCallback callback, Object state) +12 Castle.Proxies.Invocations.Controller_BeginExecute.InvokeMethodOnTarget() +131 Castle.DynamicProxy.AbstractInvocation.Proceed() +80 Abp.Domain.Uow.UnitOfWorkInterceptor.Intercept(IInvocation invocation) in D:\Halil\GitHub\aspnetboilerplate\src\Abp\Domain\Uow\UnitOfWorkInterceptor.cs:29 Castle.DynamicProxy.AbstractInvocation.Proceed() +108 Castle.Proxies.AccountControllerProxy.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +174 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +16 System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +52 System.Web.Mvc.Async.WrappedAsyncVoid1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +384 System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +103 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1586.0

I'm confused why validation is happening before the controller action method is called and how can I catch this exception to display a user friendly error instead?


9 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    ABP framework handles validation for you by default on the server side. Also, it should be handled in the client side.

    You can view ABP's validation documents for more information <a class="postlink" href="http://aspnetboilerplate.com/Pages/Documents/MVC-Controllers#validation">http://aspnetboilerplate.com/Pages/Docu ... validation</a> <a class="postlink" href="http://aspnetboilerplate.com/Pages/Documents/Validating-Data-Transfer-Objects">http://aspnetboilerplate.com/Pages/Docu ... er-Objects</a>

    By the way, did you created your project from <a class="postlink" href="http://aspnetboilerplate.com/Templates">http://aspnetboilerplate.com/Templates</a> ? If so, try setting customErrors="On" in your web.config and see if you can see validation errors on client side ?

  • User Avatar
    0
    djfuzzy created

    Yes, I have used the template. I am running ABP v0.10.3. I also read the documentation and have set CustomErrors to "On". I am still getting an Internal Server Error with the stack trace I posted above. I still am unable to figure out how to show the specific validation error on the calling form instead of the unhandled exception as shown above. I don't think my DTOs are being automatically validated as I have the call to the application service that is failing in a try/catch in my MVC controller. I would expect to get a friendly error saying Nickname is required instead of the internal server error. Here is my action method in my MVC controller:

    [HttpPost]
            [UnitOfWork]
            public virtual async Task<ActionResult> SignUp(SignUpViewModel model)
            {
                try
                {
                    CheckModelState();
                    
                    var input = new CreatePlayerWithNewUserInput()
                    {
                        User = new CreateUserInput
                        {
                            Nickname = model.Nickname,
                            EmailAddress = model.EmailAddress,
                            PasswordHashed = new PasswordHasher().HashPassword(model.Password),
                            IsActive = true
                        }
                    };
    
                    //Save user
                    await PlayerAppService.CreatePlayerWithNewUser(input);
                    await UnitOfWorkManager.Current.SaveChangesAsync();
    
                    //Directly login if possible
                    if (input.User.IsActive)
                    {
                        AbpUserManager<Tenant, Role, User>.AbpLoginResult loginResult;
                        loginResult = await GetLoginResultAsync(input.User.EmailAddress, model.Password);
    
                        if (loginResult.Result == AbpLoginResultType.Success)
                        {
                            await SignInAsync(loginResult.User, loginResult.Identity);
                            return Redirect(Url.Action("Index", "Home"));
                        }
    
                        Logger.Warn("New registered user could not be login. This should not be normally. login result: "
                            + loginResult.Result);
                    }
    
                    //If can not login, show a register result page
                    return View("SignUpResult", new SignUpResultViewModel
                    {
                        Nickname = input.User.Nickname,
                        EmailAddress = input.User.EmailAddress,
                        IsActive = input.User.IsActive
                    });
                }
                catch (UserFriendlyException ex)
                {
                    ViewBag.ErrorMessage = ex.Message;
                    ViewBag.ErrorDetails = ex.Details;
    
                    return SignUpView(model);
                }
                catch (Exception ex)
                {
                    ViewBag.ErrorMessage = ex.Message;
    
                    return SignUpView(model);
                }
            }
    

    Another thing that isn't working is when I have validation annotations in SignUpViewModel. I get a similar server error before the MVC controller action is even called. Something is not working.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    ABP actually calls CheckModelState before calling the action method of controller by default. You can add [DisableValidation] attribute to your controller or action method, if you want to do validation manually.

    You can also disable validation for all MVC controllers like this in PreInitialize of your web module.

    Configuration.Modules.AbpMvc().IsValidationEnabledForControllers = false;
    
  • User Avatar
    0
    djfuzzy created

    <cite>ismcagdas: </cite> Hi,

    ABP actually calls CheckModelState before calling the action method of controller by default. You can add [DisableValidation] attribute to your controller or action method, if you want to do validation manually.

    You can also disable validation for all MVC controllers like this in PreInitialize of your web module.

    Configuration.Modules.AbpMvc().IsValidationEnabledForControllers = false;
    

    Thank you, that's good to know.

  • User Avatar
    0
    OutdoorEd created

    I tried to use {DisableValidation] both in individual Actions in the Controller and on the entire Controller but I still get Abp.Runtime.Validation.AbpValidationException: Method arguments are not valid

    If I add
    Configuration.Modules.AbpMvc().IsValidationEnabledForControllers = false; the MVC validation works however, it means that I have disabled validation for all of the Abp Controllers as well which I don't want to do (things like creating Users/Roles/Tenants etc.)

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @OutdoorEd,

    Which version of ABP do you use ? As I remember there was an error related to this.

    Thanks.

  • User Avatar
    0
    OutdoorEd created

    I am on v3.0.0.0

  • User Avatar
    0
    OutdoorEd created

    What was the previous code issue?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    I was asking about ABP's version. You can check it in your nuget references.

    Actually I couldn't find the issue but updating ABP can solve your problem if you can update it.

    Thanks.