Access property of attribute in method - c#

I've been looking around to find 'script' to help me to access properties set at runtime from an attribute on a method. I implemented something, but properties set at runtime have only their default value. Is this even possible? I show you the code I have:
First if all, i have an interface:
public interface IRootController{}
My Controller inherits from a base controller who implements this interface.
In this controller i have a method with an attribute:
[LowerAndUpperDateTime]
public HttpResponseMessage GetAllLastResponding(string cultureinfo, Guid campaignUniqueID, int pageIndex, int pageSize, string lowerDateTime, string upperDateTime, BreakDown breakDown)
{
var test = this.GetAttribute<LowerAndUpperDateTimeAttribute>(GetCurrentMethod());
return null;
}
The code of the attribute is like this:
public class LowerAndUpperDateTimeAttribute : ActionFilterAttribute
{
public DateTime UpperDateTime;
public DateTime LowerDateTime;
public string test = "sfsdfdsf";
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionArguments.Count < 1)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest,
new[]{Errors.ObjectRequired});
}
else
{
try
{
var lowerDateTime = string.Empty;
var upperDateTime = string.Empty;
//controleren of de arguments bestaan
if (actionContext.ActionArguments.ContainsKey("lowerDateTime"))
lowerDateTime = actionContext.ActionArguments["lowerDateTime"].ToString();
if (actionContext.ActionArguments.ContainsKey("upperDateTime"))
upperDateTime = actionContext.ActionArguments["upperDateTime"].ToString();
if (string.IsNullOrEmpty(lowerDateTime) || string.IsNullOrEmpty(upperDateTime))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest,
new[]{Errors.EmptyDateTimes});
}
else{
try
{
LowerDateTime = DateTime.ParseExact(lowerDateTime, "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
UpperDateTime = DateTime.ParseExact(upperDateTime, "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
if (LowerDateTime > UpperDateTime)
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest,
new[]{Errors.LowerDateBiggerTheUpperDate});
}
catch (Exception e)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest,
new[]{e.Message});
}
}
}
catch
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, new[]{Errors.NoAccess});
}
}
}
}
The extensionmethod i created that is used in the method of my controller to access the attribute looks like this:
public static TAttribute GetAttribute<TAttribute>(this IRootController currentclass, string methodname)where TAttribute : Attribute
{
return (TAttribute)currentclass.GetType().GetMethod(methodname).GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault();
}
So this way i can access the attribute, but both datetimes have the defaultvalues, even when they got set.
Is it even possible what i'm trying here and if so, where am i wrong?
Thanks!

Related

HttpSessionStateBase losing property values of inherited type

We are using HttpSessionStateBase to store messages in a set up similar to this working example:
public class HttpSessionMessageDisplayFetch : IMessageDisplayFetch
{
protected HttpSessionStateBase _session;
private IList<ICoreMessage> messages
{
get
{
if (_session[EchoCoreConstants.MESSAGE_KEY] == null)
_session[EchoCoreConstants.MESSAGE_KEY] = new List<ICoreMessage>();
return _session[EchoCoreConstants.MESSAGE_KEY] as IList<ICoreMessage>;
}
}
public HttpSessionMessageDisplayFetch()
{
if (HttpContext.Current != null)
_session = new HttpSessionStateWrapper(HttpContext.Current.Session);
}
public void AddMessage(ICoreMessage message)
{
if (message != null)
messages.Add(message);
}
public IEnumerable<IResultPresentation> FlushMessagesAsPresentations(IResultFormatter formatter)
{
var mToReturn = messages.Select(m => m.GetPresentation(formatter)).ToList();
messages.Clear();
return mToReturn;
}
}
When we pass in a QualityExplicitlySetMessage (which inherits from ICoreMessage, see below) it is saved correctly to messages.
This is how the object looks after being inserted into the messages list, at the end of AddMessage(ICoreMessage message) above.
But when we come to access it after changing controllers the inherited member's properties are null, which causes a variety of null reference exceptions.
This is how the object now looks after we call FlushMessagesAsPresentations. I've commented out var mToReturn... as this tries to access one of these null ref properties.
I'd like to ask the following:
Why is the HttpSessionStateBase failing to capture these values taken
by the inherited type?
Is this an issue in saving to the HttpSession or in retrieving?
Is this anything to do with, as I suspect, inheritance?
Or is the fact I'm potentially calling a new controller that dependency injects the HttpSessionMessageDisplayFetch causing an issue?
I'm a first-time poster so please let me know if I'm making any kind of faux pas - Super keen to learn! Any input is very welcome.
Some potentially useful code snippets:
QualityExplicitlySetMessage
public class QualityExplicitlySetMessage : QualityChangeMessage
{
public QualityExplicitlySetMessage(IQPossession before, IQPossession after, IQEffect qEffect)
: base(before, after, qEffect)
{
IsSetToExactly = true;
}
}
QualityChangeMessage - Working example
public abstract class QualityChangeMessage : CoreMessage, IQualityChangeMessage
{
protected PossessionChange Change;
public PossessionChange GetPossessionChange()
{
return Change;
}
protected QualityChangeMessage(IQPossession before, IQPossession after, IQEffect qEffect)
{
Change = new PossessionChange(before, after, qEffect);
StoreQualityInfo(qEffect.AssociatedQuality);
}
public override IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetQualityResult(this);
}
#region IQualityChangeMessage implementation
public int LevelBefore
{
get { return Change.Before.Level; }
}
//... And so on with values dependent on the Change property.
}
CoreMessage - Working example
public abstract class CoreMessage : ICoreMessage
{
public string MessageType
{
get { return GetType().ToString(); }
}
public string ImageTooltip
{
get { return _imagetooltip; }
set { _imagetooltip = value; }
}
public string Image
{
get { return _image; }
set { _image = value; }
}
public int? RelevantQualityId { get; set; }
protected void StoreQualityInfo(Quality q)
{
PyramidNumberIncreaseLimit = q.PyramidNumberIncreaseLimit;
RelevantQualityId = q.Id;
RelevantQualityName = q.Name;
ImageTooltip = "<strong>" + q.Name + "</strong><br/>" + q.Description + "<br>" +
q.EnhancementsDescription;
Image = q.Image;
}
public virtual IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetResult(this);
}
}
UserController - Working example.
public partial class UserController : Controller
{
private readonly IMessageDisplayFetch _messageDisplayFetch;
public UserController(IMessageDisplayFetch messageDisplayFetch)
{
_messageDisplayFetch = messageDisplayFetch;
}
public virtual ActionResult MessagesForStoryletWindow()
{
var activeChar = _us.CurrentCharacter();
IEnumerable<IResultPresentation> messages;
messages = _messageDisplayFetch.FlushMessagesAsPresentations(_storyFormatter);
var vd = new MessagesViewData(messages)
{
Character = new CharacterViewData(activeChar),
};
return View(Views.Messages, vd);
}
}

pass value through custom attribute for method access

Can any one please tell me about custom attribute for method.I need to pass a string to attribute.If the string is true then i will access the method otherwise don't access the method.
I am not sure, if I understood your question wrongly. But are you talking about following kind of attribute which decorates the method. I had created this code when I was exploring attributes. I am pasting it here. Hope it helps.
In this, I have created the attribute, [Allow("Valid")] if it is valid we can call the method , else not.
namespace ConsoleApplication1
{
using System;
[AttributeUsage(AttributeTargets.All)]
public class AllowAttribute : System.Attribute
{
public readonly string SomeString;
public AllowAttribute(string someString) // your string is passed in custom attribute
{
this.SomeString = someString;
}
}
public interface IAllowAttributeInvoker
{
object AllowAttributeInvokeMethod<T>(string methodName, T classInstance, object[] parametersArray);
}
public class AllowAttributeInvoker: IAllowAttributeInvoker
{
public object AllowAttributeInvokeMethod<T>(string methodName, T classInstance, object[] parametersArray)
{
System.Reflection.MemberInfo info = typeof(T).GetMethod(methodName);
if (IsAttributeValid(info))
{
var method = (typeof (T)).GetMethod(methodName);
Console.WriteLine("Invoking method");
var result = method.Invoke(classInstance, parametersArray);
return result;
}
else
{
Console.WriteLine("Can not invoke this method.");
}
return null;
}
private static bool IsAttributeValid(MemberInfo member)
{
foreach (object attribute in member.GetCustomAttributes(true))
{
if (attribute is AllowAttribute && ((AllowAttribute)attribute).SomeString == "Valid")
{
return true;
}
}
return false;
}
}
public class EmployeeService :AllowAttributeInvoker
{
public object PaySalary()
{
return AllowAttributeInvokeMethod("PaySalaryInvoke", this, null);
}
[Allow("Valid")]
public void PaySalaryInvoke()
{
Console.WriteLine("Salary Paid.");
}
}
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
EmployeeService service = new EmployeeService();
service.PaySalary();
Console.ReadLine();
}
}
}
1-You can define public user access list:
public List<string> AccessRules = new List<string>();
2-Set user access rules in constructor :
AccessRules.AddRange(new[] { "GetCurrentDateTime", "GetCurrentDate" });
3-In secure methods check user access rule
public DateTime GetCurrentDateTime()
{
bool haveAccess = AccessRules.Any(c => c == "GetCurrentDateTime");
if (haveAccess)
{
return DateTime.Now;
}
return null;
}

Testing FluentValidation PropertyValidator

Is it possible to test a FluentValidation PropertyValidator in isolation?
I know I can test the Validator that's using the PropertyValidator for specific errors but I’d rather test true/false just on the property validator if possible.
Can this be done? If so, how?
I also wanted to test my true / false logic. It is a shame the IsValid method is protected. My work around was to create another IsValid method and have the protected IsValid call through to it.
public class MyValidator: PropertyValidator
{
public MyValidator(
string errorMessage = "default Message") : base(errorMessage)
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var stringToValidate = context.PropertyValue as String;
return IsValid(stringToValidate);
}
public bool IsValid(string stringToValidate)
{
if (stringToValidate == null)
{
return false;
}
//testing logic here
return true;
}
}
I know this has been a while, but I achieved this as follows:
Custom Validator:
public class MyValidator : PropertyValidator
{
public MyValidator ()
: base("Value must be null or between 0 and 3.")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null)
{
return true;
}
var value = (decimal)context.PropertyValue;
return value >= 0m && value <= 3m;
}
}
Test Validator:
public class TestValidator : InlineValidator<TestObject>
{
public TestValidator (params Action<TestValidator >[] actions)
{
foreach (var action in actions)
{
action(this);
}
}
}
Test Object:
public class TestObject
{
public TestObject(decimal? val)
{
this.GenericDecimal = val;
}
public decimal? GenericDecimal { get; set; }
}
Test:
[Test]
public void TestIt()
{
var validator = new TestValidator(v => v.RuleFor(obj => obj.GenericDecimal).SetValidator( new MyValidator() ));
Assert.IsTrue(validator.Validate(new TestObject(null)).IsValid);
Assert.IsTrue(validator.Validate(new TestObject(0m)).IsValid);
Assert.IsTrue(validator.Validate(new TestObject(3m)).IsValid);
Assert.IsFalse(validator.Validate(new TestObject(-1m)).IsValid);
Assert.IsFalse(validator.Validate(new TestObject(3.01m)).IsValid);
}
As for version 6.2 of FluentValidation it is possible to build the PropertyValidator.Validate() parameter due to making ValidatorSelectors globally configurable: https://github.com/JeremySkinner/FluentValidation/commit/95376c0519da1a06388be91a97fb5062fd4a162e
In the below example you see how I validate the 'puic' property of Track
Unit test:
public void ExistsInCollectionValidatorTest()
{
var track = new Track()
{
puic = "p1"
};
var sut = new ExistsInCollectionValidator<Track>();
// Build PropertyValidator.Validate() parameter
var selector = ValidatorOptions.ValidatorSelectors.DefaultValidatorSelectorFactory();
var context = new ValidationContext(track, new PropertyChain(), selector);
var propertyValidatorContext = new PropertyValidatorContext(context, PropertyRule.Create<Track,string>(t => t.puic), "puic");
var results = sut.Validate(propertyValidatorContext);
// Assertion..
}

Multiple authorization attributes that are aware of each other

I have a very simple scenario. I want to decorate my controllers/actions with a custom authorization attribute. Authorization should be granted if any of the attributes is valid. For example,
[MyAuth(1)]
[MyAuth(2)]
public class MyController : Controller
{
...
}
I cannot combine the parameters into a single authorization attribute. The above example is a simplified example, only.
If either attribute authorizes the user, I want the user to be authorized. I assumed that ActionFilterAttribute or AuthorizeAttribute would have the means to see what other filters have been executed and are waiting to be executed, but no such luck.
How can I accomplish this? Since the attributes don't seem to have any awareness, maybe an HttpModule? A custom ControllerActionInvoker?
I managed to get this to work last night. My solution is below. The attribute is pretty standard and I've trimmed the actual authorization parts. The interesting stuff happens in HasAssignedAcccessActionInvoker.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class RequiresAssignedAccess : AuthorizeAttribute
{
public int AccessType { get; private set; }
public int IdType { get; private set; }
public int IdValue { get; private set; }
public int Level { get; private set; }
public RequiresAssignedAccess(int accessType, int idType, int idValue, int level)
{
...
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!base.AuthorizeCore(httpContext))
return false;
bool retval = ...
return retval;
}
}
HasAssignedAcccessActionInvoker inherits from the standard action invoker, but I overrode the InvokeAuthorizationFilters method to add the authorization logic we need. The standard invoker just spins through the authorization filters and if any of them returns a result, it breaks the loop.
public class HasAssignedAcccessActionInvoker : ControllerActionInvoker
{
protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext authCtx = new AuthorizationContext(controllerContext, actionDescriptor);
/*
* If any of the filters are RequiresAssignedAccess, default this to false. One of them must authorize the user.
*/
bool hasAccess = !filters.Any(f => f is RequiresAssignedAccess);
foreach (IAuthorizationFilter current in filters)
{
/*
* This sets authorizationContext.Result, usually to an instance of HttpUnauthorizedResult
*/
current.OnAuthorization(authCtx);
if (current is RequiresAssignedAccess)
{
if (authCtx.Result == null)
{
hasAccess = true;
}
else if (authCtx.Result is HttpUnauthorizedResult)
{
authCtx.Result = null;
}
continue;
}
if (authCtx.Result != null)
break;
}
if (!hasAccess && authCtx.Result == null)
authCtx.Result = new HttpUnauthorizedResult();
return authCtx;
}
}
I had to look at MVC's internals with ILSpy to figure this out. For reference, this is the overridden version of that method:
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter current in filters)
{
current.OnAuthorization(authorizationContext);
if (authorizationContext.Result != null)
{
break;
}
}
return authorizationContext;
}
Lastly, to wire this up and make everything possible, our controllers inherit from BaseController, which now returns the new invoker.
public class BaseController : Controller
{
protected override IActionInvoker CreateActionInvoker()
{
return new HasAssignedAcccessActionInvoker();
}
}
As far as I know, you cannot chain [Authorize] attributes in the manner that you want because they all have to pass (AND) and not (OR) behavior. However, the combining of the items into one does not cause you to have to do some magic string manipulation, regardless of the number of parameters that you need to pass to it. You can define your own set of parameters that are available to the Authorize attribute.
public class SuperCoolAuthorize : AuthorizationAttribute
{
public string Parameter1{get;set;}
public string Parameter2{get;set;}
public int Parameter3{get;set;}
public string Parameter4{get;set;}
public override void OnAuthorization(AuthorizationContext filterContext)
{
// your custom behaviour
}
}
And on your controller/action method
[Authorize(Parameter1 = "Foo", Parameter2 = "Bar", Parameter3 = 47, Parameter4 = string.Empty)
public ActionResult MyControllerAction(){
...
}
A great post on some other considerations on custom Authorizing attributes I came across in helping to formulate this answer.
public class AuthUserAttribute : AuthorizeAttribute {
public string[] SecurityGroups;
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext) {
bool valid = false;
var user = UserInformation.Current;
if (user.SecurityGroups.Select(x => x).Intersect(this.SecurityGroups).Any()) {
valid = true;
}
if (user.SecurityGroups.Select(x => x).Intersect(new string[] { "IT Administrators" }).Any()) {
valid = true;
}
return valid;
}
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!this.AuthorizeCore(filterContext.HttpContext)) {
if (UserInformation.Current.SecurityGroups.Count == 0) {
filterContext.Result = new RedirectResult(string.Format("/oa?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
}
else {
filterContext.Result = new RedirectResult(string.Format("/oa/user/permissions?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
}
}
else {
base.OnAuthorization(filterContext);
}
}
}
then I decorate with
[AuthUser(SecurityGroups = new string[] { "Data1", "Data2" })]
public ActionResult ForYourEyesOnly() {
}
We'll see if anyone catches the Bond reference. LOL

ModelState.IsValid even when it should not be?

I have API where I need to validate my user model. I choose an approach where I create different classes for Create/Edit actions to avoid mass-assignment and divide validation and actual model apart.
I don't know why but ModelState.IsValid returns true even when it should not. Am I doing something wrong?
Controller
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);
}
Model
public class UserCreate
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Name { get; set; }
}
Debug proof
The ModelState.IsValid internally checks the Values.All(modelState => modelState.Errors.Count == 0) expression.
Because there was no input the Values collection will be empty so ModelState.IsValid will be true.
So you need to explicitly handle this case with:
if (user != null && ModelState.IsValid)
{
}
Whether this is a good or bad design decision that if you validate nothing it will true is a different question...
Here is an action filter to check for null models or invalid models. (so you dont have to write the check on every action)
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);
}
}
}
}
You can register it globally:
config.Filters.Add(new ValidateViewModelAttribute());
Or use it on demand on classes/actions
[ValidateViewModel]
public class UsersController : ApiController
{ ...
I wrote a custom filter which not only ensures that all non optional object properties are passed, but also checks if model state is valid:
[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;
}
}
And I put it in global filter for all Web API actions:
config.Filters.Add (new ValidateModelAttribute ());
Updated slightly for asp.net core...
[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)
{
//...
}
This happened to me, and in my case, I had to change using Microsoft.Build.Framework; to using System.ComponentModel.DataAnnotations; (and add the reference).
I was looking for a solution to this problem and came out here first. After some further research I have realized the following solution:
How do you use my solution?
You can register it globally:
config.Filters.Add(new ValidateModelStateAttribute());
Or use it on demand for a class
[ValidateModelState]
public class UsersController : ApiController
{...
or for a methode
[ValidateModelState]
public IHttpActionResult Create([Required] UserModel data)
{...
As you can see, a [System.ComponentModel.DataAnnotations.Required] atribute has been placed in the method parameter.
This indicates that the model is required and can not be null.
You can also use with a custom message:
[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 "ModelState.IsValid returns true even when it should not" problem can also appear if you forgot to add getters and setters on your model (OP didn't forget, but I did which led me to this question). I hope it's ok to provide solutions that have the same symptoms but are slightly different than OP's code:
Wrong:
public class UserRegisterModel
{
[Required]
public string Login; // WRONG
[Required]
public string Password; // WRONG
}
Good:
public class UserRegisterModel
{
[Required]
public string Login { get; set; }
[Required]
public string Password { get; set; }
}
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)

Categories