49

我有一个具有必需属性的模型对象

public class ApiPing
{
    [Required]
    public DateTime ClientTime { get; set; }

    public DateTime ServerTime { get; set; }
}

我有一个检查模型状态的控制器方法。

public IHttpActionResult Ping(ApiPing model)
{    
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    model.ServerTime = DateTime.UtcNow;

    return Ok(model);
}

如果我向操作方法提交一个正确的请求(带有模型),我会从 ModeState.IsValid (true) 获得正确的值。但是,当我提交无效请求(没有模型,因此模型为空)时,我得到一个错误的 ModelState.IsValid(也是正确的)。

我可以简单地检查我的代码中的模型是否为空,但这有异味。这是预期的“功能”还是 ModelState 验证中的错误?难道我做错了什么 ?我期待太多了吗?

4

4 回答 4

24

我之前也遇到过同样的问题,答案已经在一些论坛上找到了,甚至在 SO:ModelState.IsValid 即使它不应该是?

您还可以添加自定义过滤器来验证(无效)缺失的字段和/或空值 http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet -web-api

http://www.strathweb.com/2012/10/clean-up-your-web-api-controllers-with-model-validation-and-null-check-filters/

于 2013-12-02T06:20:46.610 回答
21

这是一个用于检查空模型或无效模型的操作过滤器。

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
 { ...
于 2014-09-14T03:38:35.743 回答
4
  1. 将模型声明为结构
  2. 将所有必需属性的类型更改为可为空
于 2015-11-07T06:40:24.843 回答
4

我在这里找到了这个问题的提示。所以我会在这里给出我的解决方案。

你如何使用我的解决方案?您可以在全球范围内注册它:

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);
            });
        }
    }
}
于 2018-07-02T12:27:22.130 回答