我有API需要验证我的用户模型。我选择了一种方法,我为创建/编辑操作创建了不同的类,以避免质量分配和划分验证以及实际模型分开。
我不知道为什么,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; }
}
答案 0 :(得分:68)
ModelState.IsValid
在内部检查Values.All(modelState => modelState.Errors.Count == 0)
表达式。
由于没有输入,Values
集合将为空,因此ModelState.IsValid
将为true
。
所以你需要用以下方法明确处理这个案例:
if (user != null && ModelState.IsValid)
{
}
这是一个好的还是坏的设计决定,如果你什么都不验证那就是一个不同的问题......
答案 1 :(得分:24)
这是一个动作过滤器,用于检查空模型或无效模型。 (所以你不必为每一个动作都写支票)
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
{ ...
答案 2 :(得分:12)
我写了一个自定义过滤器,它不仅确保传递所有非可选对象属性,还检查模型状态是否有效:
[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 ());
答案 3 :(得分:4)
稍微更新了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)
{
//...
}
答案 4 :(得分:2)
这件事发生在我身上,就我而言,我必须将using Microsoft.Build.Framework;
更改为using System.ComponentModel.DataAnnotations;
(并添加参考资料)。
答案 5 :(得分:2)
我一直在寻找解决此问题的方法,然后首先来到这里。经过一些进一步的研究,我实现了以下解决方案:
您如何使用我的解决方案? 您可以在全球范围内注册:
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);
});
}
}
}
答案 6 :(得分:2)
有一个简单的解决方案可以解决您的问题
public class UserCreate
{
[Required(AllowEmptyStrings = false)]
public string Username { get; set; }
}
此处 AllowEmptyStrings = false 可用于验证
答案 7 :(得分:0)
我所做的是创建Attribute
以及ActionFilter
和Extension Method
以避免空模型。
扩展方法查找具有NotNull
属性的参数,并检查它们是否为null,如果为true,则将它们实例化并设置在ActionArguments
属性中。
此解决方案可在此处找到:https://gist.github.com/arielmoraes/63a39a758026b47483c405b77c3e96b9
答案 8 :(得分:0)
尝试
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
在startup.cs
文件的ConfigureServices()
答案 9 :(得分:-2)
这个问题发生在我身上。我不知道为什么会这么简单,只需更改你的动作对象名称(UserCreate User)就像其他人一样(UserCreate User_create)