it-swarm.dev

ModelState.IsValid حتى عندما لا ينبغي أن يكون؟

لدي واجهة برمجة التطبيقات حيث أحتاج إلى التحقق من صحة نموذج المستخدم الخاص بي. اخترت منهجًا حيث أقوم بإنشاء فئات مختلفة لإجراءات إنشاء/تحرير لتجنب الاحالة الجماعية وتقسيم التحقق من الصحة والنموذج الفعلي.

لا أعرف لماذا ولكن ModelState.IsValid تُرجع بشكل صحيح حتى عندما لا ينبغي لها ذلك. أفعل شيئا خاطئا؟

مراقب

public HttpResponseMessage Post(UserCreate user)
{
    if (ModelState.IsValid) // It's valid even when user = null
    {
        var newUser = new User
        {
            Username = user.Username,
            Password = user.Password,
            Name = user.Name
        };
        _db.Users.Add(newUser);
        _db.SaveChanges();
        return Request.CreateResponse(HttpStatusCode.Created, new { newUser.Id, newUser.Username, newUser.Name });
    }
    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}

نموذج

public class UserCreate
{
    [Required]
    public string Username { get; set; }
    [Required]
    public string Password { get; set; }
    [Required]
    public string Name { get; set; }
}

تصحيح الأخطاء

proof

59
sed

يتحقق ModelState.IsValid داخليًا من التعبير Values.All(modelState => modelState.Errors.Count == 0).

نظرًا لعدم وجود أي إدخال ، ستكون المجموعة Values فارغة ، لذا ModelState.IsValid ستكون true.

لذلك تحتاج إلى معالجة هذه الحالة بشكل صريح باستخدام:

if (user != null && ModelState.IsValid)
{

}

سواء كان هذا قرارًا جيدًا أو سيئًا للتصميم ، فإنه في حالة التحقق من صحة أي شيء ، سيكون هذا صحيحًا سؤال مختلف ...

69
nemesv

فيما يلي عامل تصفية إجراء للبحث عن النماذج الخالية أو النماذج غير الصالحة. (حتى لا تضطر إلى كتابة الشيك في كل عمل)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace Studio.Lms.TrackingServices.Filters
{
    public class ValidateViewModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ActionArguments.Any(kv => kv.Value == null)) {
                actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Arguments cannot be null");
            }

            if (actionContext.ModelState.IsValid == false) {
                actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }
}

يمكنك تسجيله عالميا:

config.Filters.Add(new ValidateViewModelAttribute());

أو استخدامها عند الطلب على الطبقات/الإجراءات

 [ValidateViewModel]
 public class UsersController : ApiController
 { ...
24
Jose Ch.

كتبت مرشحًا مخصصًا لا يضمن فقط تمرير جميع خصائص الكائنات غير الاختيارية ، ولكن أيضًا يتحقق مما إذا كانت حالة النموذج صالحة:

[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class ValidateModelAttribute : ActionFilterAttribute
{
    private static readonly ConcurrentDictionary<HttpActionDescriptor, IList<string>> NotNullParameterNames =
        new ConcurrentDictionary<HttpActionDescriptor, IList<string>> ();


    /// <summary>
    /// Occurs before the action method is invoked.
    /// </summary>
    /// <param name="actionContext">The action context.</param>
    public override void OnActionExecuting (HttpActionContext actionContext)
    {
        var not_null_parameter_names = GetNotNullParameterNames (actionContext);
        foreach (var not_null_parameter_name in not_null_parameter_names)
        {
            object value;
            if (!actionContext.ActionArguments.TryGetValue (not_null_parameter_name, out value) || value == null)
                actionContext.ModelState.AddModelError (not_null_parameter_name, "Parameter \"" + not_null_parameter_name + "\" was not specified.");
        }


        if (actionContext.ModelState.IsValid == false)
            actionContext.Response = actionContext.Request.CreateErrorResponse (HttpStatusCode.BadRequest, actionContext.ModelState);
    }


    private static IList<string> GetNotNullParameterNames (HttpActionContext actionContext)
    {
        var result = NotNullParameterNames.GetOrAdd (actionContext.ActionDescriptor,
                                                     descriptor => descriptor.GetParameters ()
                                                                             .Where (p => !p.IsOptional && p.DefaultValue == null &&
                                                                                          !p.ParameterType.IsValueType &&
                                                                                          p.ParameterType != typeof (string))
                                                                             .Select (p => p.ParameterName)
                                                                             .ToList ());

        return result;
    }
}

وأضعها في فلتر عام لجميع إجراءات Web API:

config.Filters.Add (new ValidateModelAttribute ());
12
Michael Logutov

تم التحديث قليلاً في asp.net الأساسية ...

[AttributeUsage(AttributeTargets.Method)]
public sealed class CheckRequiredModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var requiredParameters = context.ActionDescriptor.Parameters.Where(
            p => ((ControllerParameterDescriptor)p).ParameterInfo.GetCustomAttribute<RequiredModelAttribute>() != null).Select(p => p.Name);

        foreach (var argument in context.ActionArguments.Where(a => requiredParameters.Contains(a.Key, StringComparer.Ordinal)))
        {
            if (argument.Value == null)
            {
                context.ModelState.AddModelError(argument.Key, $"The argument '{argument.Key}' cannot be null.");
            }
        }

        if (!context.ModelState.IsValid)
        {
            var errors = context.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
            context.Result = new BadRequestObjectResult(errors);
            return;
        }

        base.OnActionExecuting(context);
    }
}

[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RequiredModelAttribute : Attribute
{
}

services.AddMvc(options =>
{
    options.Filters.Add(typeof(CheckRequiredModelAttribute));
});

public async Task<IActionResult> CreateAsync([FromBody][RequiredModel]RequestModel request, CancellationToken cancellationToken)
{
    //...
}
5
James Law

هناك حل بسيط لمشكلتك

public class UserCreate
{
    [Required(AllowEmptyStrings = false)]
    public string Username { get; set; }
}

هنا AllowEmptyStrings = false يمكن استخدامها للتحقق من صحة الخاص بك

2
Yagnesh Khamar

كنت أبحث عن حل لهذه المشكلة وخرجت هنا أولاً. بعد بعض الأبحاث الإضافية ، أدركت الحل التالي:

كيف يمكنك استخدام حل بلدي؟ يمكنك تسجيله عالميا:

config.Filters.Add(new ValidateModelStateAttribute());

أو استخدامه عند الطلب لفئة

[ValidateModelState]
public class UsersController : ApiController
{...

أو لميثود

[ValidateModelState]
public IHttpActionResult Create([Required] UserModel data)
{...

كما ترى ، تم وضع سمة [System.ComponentModel.DataAnnotations.Required] في معلمة الطريقة. يشير هذا إلى أن النموذج مطلوب ولا يمكن أن يكون null.

يمكنك أيضًا استخدام رسالة مخصصة:

[ValidateModelState]
public IHttpActionResult Create([Required(ErrorMessage = "Custom message")] UserModel data)
{...

هنا هو الكود الخاص بي:

using System;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace your_base_namespace.Web.Http.Filters
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
    public class ValidateModelStateAttribute : ActionFilterAttribute
    {
        private delegate void ValidateHandler(HttpActionContext actionContext);

        private static readonly ConcurrentDictionary<HttpActionBinding, ValidateHandler> _validateActionByActionBinding;

        static ValidateModelStateAttribute()
        {
            _validateActionByActionBinding = new ConcurrentDictionary<HttpActionBinding, ValidateHandler>();
        }

        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            GetValidateHandler(actionContext.ActionDescriptor.ActionBinding)(actionContext);

            if (actionContext.ModelState.IsValid)
                return;

            actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
        }

        private ValidateHandler GetValidateHandler(HttpActionBinding actionBinding)
        {
            ValidateHandler validateAction;

            if (!_validateActionByActionBinding.TryGetValue(actionBinding, out validateAction))
                _validateActionByActionBinding.TryAdd(actionBinding, validateAction = CreateValidateHandler(actionBinding));

            return validateAction;
        }

        private ValidateHandler CreateValidateHandler(HttpActionBinding actionBinding)
        {
            ValidateHandler handler = new ValidateHandler(c => { });

            var parameters = actionBinding.ParameterBindings;

            for (int i = 0; i < parameters.Length; i++)
            {
                var parameterDescriptor = (ReflectedHttpParameterDescriptor)parameters[i].Descriptor;
                var attribute = parameterDescriptor.ParameterInfo.GetCustomAttribute<RequiredAttribute>(true);

                if (attribute != null)
                    handler += CreateValidateHandler(attribute, parameterDescriptor.ParameterName);
            }

            return handler;
        }

        private static ValidateHandler CreateValidateHandler(ValidationAttribute attribute, string name)
        {            
            return CreateValidateHandler(attribute, new ValidationContext(new object()) { MemberName = name });
        }

        private static ValidateHandler CreateValidateHandler(ValidationAttribute attribute, ValidationContext context)
        {
            return new ValidateHandler(actionContext =>
            {
                object value;
                actionContext.ActionArguments.TryGetValue(context.MemberName, out value);

                var validationResult = attribute.GetValidationResult(value, context);
                if (validationResult != null)
                    actionContext.ModelState.AddModelError(context.MemberName, validationResult.ErrorMessage);
            });
        }
    }
}
2
Roberto B

حدث هذا بالنسبة لي ، وفي حالتي ، اضطررت إلى تغيير using Microsoft.Build.Framework; إلى using System.ComponentModel.DataAnnotations; (وإضافة المرجع).

2
james

محاولة

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

في startup.cs لملف ConfigureServices()

0
Iran Canul

ما فعلته هو إنشاء Attribute مع ActionFilterExtension Method لتجنب النماذج الخالية.

تبحث طريقة الامتداد عن معلمات ذات السمة NotNull وتحقق مما إذا كانت فارغة ، وإذا كانت صحيحة ، فستتم إنشاء مثيل لها وتعيينها في الخاصية ActionArguments.

يمكن العثور على هذا الحل هنا: https://Gist.github.com/arielmoraes/63a39a758026b47483c405b77c3e96b9

0
Ariel Moraes