ModelState.IsValid가 아니어야 할 때에도?
사용자 모델의 유효성을 검사하는 데 필요한 API가 있습니다. 대량 할당을 방지하고 유효성 검사와 실제 모델을 분리하기 위해 Create / Edit 작업에 대해 다른 클래스를 만드는 접근 방식을 선택합니다.
이유는 모르겠지만 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; }
}
디버그 증명
는 ModelState.IsValid
내부적으로 확인 Values.All(modelState => modelState.Errors.Count == 0)
식.
아무런 입력이 없었다 때문에 Values
컬렉션 비어 그렇게 ModelState.IsValid
될 것입니다 true
.
따라서 다음을 사용하여이 경우를 명시 적으로 처리해야합니다.
if (user != null && ModelState.IsValid)
{
}
이것이 아무 것도 검증하지 않으면 사실이 될 것이라는 좋은 디자인 결정인지 나쁜 디자인 결정인지는 다른 질문입니다.
다음은 null 모델 또는 유효하지 않은 모델을 확인하는 작업 필터입니다. (따라서 모든 작업에 대해 수표를 작성할 필요가 없습니다)
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
{ ...
선택적이 아닌 모든 개체 속성이 전달되도록 할뿐만 아니라 모델 상태가 유효한지 확인하는 사용자 지정 필터를 작성했습니다.
[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;
}
}
그리고 모든 웹 API 작업에 대해 전역 필터에 넣었습니다.
config.Filters.Add (new ValidateModelAttribute ());
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)
{
//...
}
이것은 나에게 일어 났고 내 경우에는 (그리고 참조를 추가) 변경 using Microsoft.Build.Framework;
해야했습니다 using System.ComponentModel.DataAnnotations;
.
나는이 문제에 대한 해결책을 찾고 있었고 여기에서 먼저 나왔습니다. 추가 연구 끝에 다음과 같은 해결책을 깨달았습니다.
내 솔루션을 어떻게 사용합니까? 전역 적으로 등록 할 수 있습니다.
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)
{...
Here is my code:
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);
});
}
}
}
There is a simple Solution for your problem
public class UserCreate
{
[Required(AllowEmptyStrings = false)]
public string Username { get; set; }
}
Here AllowEmptyStrings = false can be used for your validation
Try
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
in the startup.cs
file's ConfigureServices()
What I did was to create an Attribute
along with an ActionFilter
and a Extension Method
to avoid null models.
The extension method looks for parameters with the NotNull
attribute and check if they are null, if true, they are instantiated and set in the ActionArguments
property.
This solution can be found here: https://gist.github.com/arielmoraes/63a39a758026b47483c405b77c3e96b9
this issue happened to me .i do not know why but take it easy just change your action Object name(UserCreate User) by some other like (UserCreate User_create)
참고 URL : https://stackoverflow.com/questions/17923622/modelstate-isvalid-even-when-it-should-not-be
'developer tip' 카테고리의 다른 글
Keras : 커널과 활동 정규화의 차이점 (0) | 2020.11.25 |
---|---|
두 파일을 한 줄씩 일관되게 병합하는 방법 (0) | 2020.11.24 |
n 개의 결과 만 반환하는 SQLAlchemy 쿼리? (0) | 2020.11.24 |
Xcode에서 이미지 크기는 @ 1x, @ 2x 및 @ 3x 여야합니까? (0) | 2020.11.24 |
64 비트 시스템에서 Java 32 비트 또는 64 비트의 int 크기가 있습니까? (0) | 2020.11.24 |