ASP.NET MVC 4 RemoteAttribute action name - c#

Is it possible to get action name of System.Web.Mvc.RemoteAttribute object. (Initialized at constructor stage)
GetUrl() method and RouteData property are protected. Are there any hints?

With reflection you can get protected properties.
The better solution I think is to create a new attribute class derived from RemoteAttribute and add some public methods/properties which return the Url and RouteData. For example:
public class MyRemoteAttribute: System.Web.Mvc.RemoteAttribute
{
public string GetUrlPublic()
{
return this.GetUrl();
}
public RouteValueDictionary GetRouteData()
{
return this.RouteData;
}
}

Related

Access Route values in ValidationAttribute

My route is defined as http:/.../home/index/1 which is (controller/action/id).
ViewModel is as below..
public class TestVM
{
[CustomValidation]
public string name{get; set;}
}
public class CustomValidation : ValidationAttribute
{
protected override ValidationResult? IsValid (object? value, validationContext)
{
var vm = (TestVM)validationContext.ObjectInstance;
//how to get the route value here?
}
}
To validate Name property, I need to have value of Id. To access route value in the IsValid method, I defined one more property Id in the TestVM.
Is there a way to access the Id in the IsValid without defining in the TestVM?
Is there a way to access the Id in the IsValid without defining in the
TestVM?
Seems you are trying to access parameter which has been passed to controller route so that, you can get those values inside your CustomValidation class
Well, using IHttpContextAccessor we can get all route value from which are available within HttpContext.Request. Thus, you can access property name of member of TestVM class.
Access Route Value Inside ValidationAttribute:
You can implement in following way:
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var vm = context.ObjectInstance as TestVM;
var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>()
var getIdFromRouteValue = httpContextAccessor.HttpContext.Request.Query["Id"];
var getNameFromRouteValue = httpContextAccessor.HttpContext.Request.Query["name"];
vm.Id = getIdFromRouteValue;
vm.name = getNameFromRouteValue;
return ValidationResult.Success;
}
}
Program.cs:
IHttpContextAccessor requires to register AddHttpContextAccessor() service on your program.cs file. You can do as following:
builder.Services.AddHttpContextAccessor();
Note: Please make sure you have followed the correct order. You can get more details on our official document here.
Output:

Class type object as HttpGet

I am trying to create an action that would look like controller/action?param1=val&param2=val with the HttpGet annotation.
What I have is:
[HttpGet]
public IActionResult Index(SomeClass obj)
{
// do stuff
return View(something);
}
I can access the action via controller/Index?obj.param1=val&obj.param2=val, but is there a way to avoid obj.param1 and obj.param2 in the query string and have something like controller/Index?page=val&amount=val.
Putting those parameters in the annotation like this didn't work: [HttpGet("/page={obj.subobject.param1}&amount={obj.subobject.param2}")]
Assuming the default model binding setup, you can just pass the parameter names directly and ASP.NET Core will automatically put the values into the SomeClass object:
public IActionResult Test(SomeClass obj)
{
return Json(obj);
}
public class SomeClass
{
public string Foo { get; set; }
public string Bar { get; set; }
}
When opening the URL /Home/Test?foo=baz&bar=qux you will now see that the object is properly filled with the Foo and Bar properties.

Middleware for filtering asp.net core REST methods

I have a .net core application with several REST operations (see code below), similar to the following:
namespace Controllers
{
[Route("system")]
public class SystemController : Controller
{
// This is a public access method
[HttpGet("dictionaries/{name}")]
public List GetDictionary(HttpRequestMessage requestMsg, string name)
{
// etc
}
// This function shall be accessible only by an admin role
[AdminRole]
[HttpPost("dictionaries/{name}")]
public IActionResult PostDictionary(HttpRequestMessage requestMsg, string name)
{
// etc
}
}
}
I want to flag some operations to be accessible only by certain roles (i.e. admin). An elegant way to do it is using attributes.
Now I want to determine what would be the correct Middleware implementation for trapping the C# method to be invoked according to the URL, and fetch the role attribute (if any) by using reflection, so I can block unauthorized calls.
Please advice.
I want to draw attention that approach below is only if you for some reason does not want to use build-in Role based Authorization (as marked in comment to question).
If you create a global Action Filter (that is a part of MVC and so can operate with "controller logic"), you can get all needed information from ActionExecutingContext:
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//Provides information about an action method, such as its name, controller, parameters, attributes, and filters.
var actionDescriptor = context.ActionDescriptor;
//Gets the controller instance containing the action.
var controller = context.Controller;
// Gets the arguments to pass when invoking the action. Keys are parameter names.
var actionArgs = context.ActionArguments;
}
...
}
context.ActionDescriptor can be casted to ControllerActionDescriptor. That allows to directly use the following properties:
public class ControllerActionDescriptor : ActionDescriptor
{
public string ControllerName { get; set; }
public virtual string ActionName { get; set; }
public MethodInfo MethodInfo { get; set; }
public TypeInfo ControllerTypeInfo { get; set; }
...
}
Not sure that you can use middleware for that, as controllers is part of MVC middleware. If you place your middleware before it, there is no "controller" logic at that pipeline step yet. And if after - it is too late for what you want.

Inheritance in action method parameters

I have the following classes:
class SomethingBase
{
public string SharedProperty { get; set; }
}
class ChildClassOne : SomethingBase
{
public string SpecificPropertyOne { get; set; }
}
class ChildClassTwo : SomethingBase
{
public string SpecificPropertyTwo { get; set; }
}
And I have ASP.NET MVC View which has two HTML-forms. These forms are calling the same action method.
This action method should receive any of two SomethingBase class derivatives.
However, if I create single parameter like SomethingBase param, then only the SharedProperty is received. This behavior can be explained by binding mechanism of ASP.NET MVC.
To make my action method work I created the next definition:
public ActionResult(ChildClassOne param1, ChildClassTwo param2)
SharedProperty goes to both params, but specific properties are populated only for object, which was actually passed from view. It works, but I don't think that this is the only solution.
Are there some best-practice solutions for this situation?
You should create a view model for each action since they are not alike. There's really no reason to try to use a base class in this case.
Method TryUpdateModel of Controller class make it work. However, this way is not pretty elegant.
...
public ActionResult Save(FormCollection collection)
{
SomethingBase model = null;
if (collection.AllKeys.Contains("SpecificOne"))
{
model = new ChildOne();
TryUpdateModel<ChildOne>((ChildOne)model, collection);
}
else
{
model = new ChildTwo();
TryUpdateModel<ChildTwo>((ChildTwo)model, collection);
}
...

Disable Required validation attribute under certain circumstances

I was wondering if it is possible to disable the Required validation attribute in certain controller actions. I am wondering this because on one of my edit forms I do not require the user to enter values for fields that they have already specified previously. However I then implement logic that when they enter a value it uses some special logic to update the model, such as hashing a value etc.
Any sugestions on how to get around this problem?
EDIT:
And yes client validation is a problem here to, as it will not allow them to submit the form without entering a value.
This problem can be easily solved by using view models. View models are classes that are specifically tailored to the needs of a given view. So for example in your case you could have the following view models:
public UpdateViewView
{
[Required]
public string Id { get; set; }
... some other properties
}
public class InsertViewModel
{
public string Id { get; set; }
... some other properties
}
which will be used in their corresponding controller actions:
[HttpPost]
public ActionResult Update(UpdateViewView model)
{
...
}
[HttpPost]
public ActionResult Insert(InsertViewModel model)
{
...
}
If you just want to disable validation for a single field in client side then you can override the validation attributes as follows:
#Html.TextBoxFor(model => model.SomeValue,
new Dictionary<string, object> { { "data-val", false }})
I know this question has been answered a long time ago and the accepted answer will actually do the work. But there's one thing that bothers me: having to copy 2 models only to disable a validation.
Here's my suggestion:
public class InsertModel
{
[Display(...)]
public virtual string ID { get; set; }
...Other properties
}
public class UpdateModel : InsertModel
{
[Required]
public override string ID
{
get { return base.ID; }
set { base.ID = value; }
}
}
This way, you don't have to bother with client/server side validations, the framework will behave the way it's supposed to. Also, if you define a [Display] attribute on the base class, you don't have to redefine it in your UpdateModel.
And you can still use these classes the same way:
[HttpPost]
public ActionResult Update(UpdateModel model)
{
...
}
[HttpPost]
public ActionResult Insert(InsertModel model)
{
...
}
You can remove all validation off a property with the following in your controller action.
ModelState.Remove<ViewModel>(x => x.SomeProperty);
#Ian's comment regarding MVC5
The following is still possible
ModelState.Remove("PropertyNameInModel");
Bit annoying that you lose the static typing with the updated API. You could achieve something similar to the old way by creating an instance of HTML helper and using NameExtensions Methods.
Client side
For disabling validation for a form, multiple options based on my research is given below. One of them would would hopefully work for you.
Option 1
I prefer this, and this works perfectly for me.
(function ($) {
$.fn.turnOffValidation = function (form) {
var settings = form.validate().settings;
for (var ruleIndex in settings.rules) {
delete settings.rules[ruleIndex];
}
};
})(jQuery);
and invoking it like
$('#btn').click(function () {
$(this).turnOffValidation(jQuery('#myForm'));
});
Option 2
$('your selector here').data('val', false);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
Option 3
var settings = $.data($('#myForm').get(0), 'validator').settings;
settings.ignore = ".input";
Option 4
$("form").get(0).submit();
jQuery('#createForm').unbind('submit').submit();
Option 5
$('input selector').each(function () {
$(this).rules('remove');
});
Server Side
Create an attribute and mark your action method with that attribute. Customize this to adapt to your specific needs.
[AttributeUsage(AttributeTargets.All)]
public class IgnoreValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
foreach (var modelValue in modelState.Values)
{
modelValue.Errors.Clear();
}
}
}
A better approach has been described here Enable/Disable mvc server side validation dynamically
Personally I would tend to use the approach Darin Dimitrov showed in his solution.
This frees you up to be able to use the data annotation approach with validation AND have separate data attributes on each ViewModel corresponding to the task at hand.
To minimize the amount of work for copying between model and viewmodel you should look at AutoMapper or ValueInjecter. Both have their individual strong points, so check them both.
Another possible approach for you would be to derive your viewmodel or model from IValidatableObject. This gives you the option to implement a function Validate.
In validate you can return either a List of ValidationResult elements or issue a yield return for each problem you detect in validation.
The ValidationResult consists of an error message and a list of strings with the fieldnames. The error messages will be shown at a location near the input field(s).
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if( NumberField < 0 )
{
yield return new ValidationResult(
"Don't input a negative number",
new[] { "NumberField" } );
}
if( NumberField > 100 )
{
yield return new ValidationResult(
"Don't input a number > 100",
new[] { "NumberField" } );
}
yield break;
}
The cleanest way here I believe is going to disable your client side validation and on the server side you will need to:
ModelState["SomeField"].Errors.Clear (in your controller or create an action filter to remove errors before the controller code is executed)
Add ModelState.AddModelError from your controller code when you detect a violation of your detected issues.
Seems even a custom view model here wont solve the problem because the number of those 'pre answered' fields could vary. If they dont then a custom view model may indeed be the easiest way, but using the above technique you can get around your validations issues.
this was someone else's answer in the comments...but it should be a real answer:
$("#SomeValue").removeAttr("data-val-required")
tested on MVC 6 with a field having the [Required] attribute
answer stolen from https://stackoverflow.com/users/73382/rob above
I was having this problem when I creating a Edit View for my Model and I want to update just one field.
My solution for a simplest way is put the two field using :
<%: Html.HiddenFor(model => model.ID) %>
<%: Html.HiddenFor(model => model.Name)%>
<%: Html.HiddenFor(model => model.Content)%>
<%: Html.TextAreaFor(model => model.Comments)%>
Comments is the field that I only update in Edit View, that not have Required Attribute.
ASP.NET MVC 3 Entity
AFAIK you can not remove attribute at runtime, but only change their values (ie: readonly true/false) look here for something similar .
As another way of doing what you want without messing with attributes I will go with a ViewModel for your specific action so you can insert all the logic without breaking the logic needed by other controllers.
If you try to obtain some sort of wizard (a multi steps form) you can instead serialize the already compiled fields and with TempData bring them along your steps. (for help in serialize deserialize you can use MVC futures)
What #Darin said is what I would recommend as well. However I would add to it (and in response to one of the comments) that you can in fact also use this method for primitive types like bit, bool, even structures like Guid by simply making them nullable. Once you do this, the Required attribute functions as expected.
public UpdateViewView
{
[Required]
public Guid? Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int? Age { get; set; }
[Required]
public bool? IsApproved { get; set; }
//... some other properties
}
As of MVC 5 this can be easily achieved by adding this in your global.asax.
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
I was looking for a solution where I can use the same model for an insert and update in web api. In my situation is this always a body content. The [Requiered] attributes must be skipped if it is an update method.
In my solution, you place an attribute [IgnoreRequiredValidations] above the method. This is as follows:
public class WebServiceController : ApiController
{
[HttpPost]
public IHttpActionResult Insert(SameModel model)
{
...
}
[HttpPut]
[IgnoreRequiredValidations]
public IHttpActionResult Update(SameModel model)
{
...
}
...
What else needs to be done?
An own BodyModelValidator must becreated and added at the startup.
This is in the HttpConfiguration and looks like this: config.Services.Replace(typeof(IBodyModelValidator), new IgnoreRequiredOrDefaultBodyModelValidator());
using Owin;
using your_namespace.Web.Http.Validation;
[assembly: OwinStartup(typeof(your_namespace.Startup))]
namespace your_namespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
Configuration(app, new HttpConfiguration());
}
public void Configuration(IAppBuilder app, HttpConfiguration config)
{
config.Services.Replace(typeof(IBodyModelValidator), new IgnoreRequiredOrDefaultBodyModelValidator());
}
...
My own BodyModelValidator is derived from the DefaultBodyModelValidator. And i figure out that i had to override the 'ShallowValidate' methode. In this override i filter the requierd model validators.
And now the IgnoreRequiredOrDefaultBodyModelValidator class and the IgnoreRequiredValidations attributte class:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.Validation;
namespace your_namespace.Web.Http.Validation
{
public class IgnoreRequiredOrDefaultBodyModelValidator : DefaultBodyModelValidator
{
private static ConcurrentDictionary<HttpActionBinding, bool> _ignoreRequiredValidationByActionBindingCache;
static IgnoreRequiredOrDefaultBodyModelValidator()
{
_ignoreRequiredValidationByActionBindingCache = new ConcurrentDictionary<HttpActionBinding, bool>();
}
protected override bool ShallowValidate(ModelMetadata metadata, BodyModelValidatorContext validationContext, object container, IEnumerable<ModelValidator> validators)
{
var actionContext = validationContext.ActionContext;
if (RequiredValidationsIsIgnored(actionContext.ActionDescriptor.ActionBinding))
validators = validators.Where(v => !v.IsRequired);
return base.ShallowValidate(metadata, validationContext, container, validators);
}
#region RequiredValidationsIsIgnored
private bool RequiredValidationsIsIgnored(HttpActionBinding actionBinding)
{
bool ignore;
if (!_ignoreRequiredValidationByActionBindingCache.TryGetValue(actionBinding, out ignore))
_ignoreRequiredValidationByActionBindingCache.TryAdd(actionBinding, ignore = RequiredValidationsIsIgnored(actionBinding.ActionDescriptor as ReflectedHttpActionDescriptor));
return ignore;
}
private bool RequiredValidationsIsIgnored(ReflectedHttpActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false;
return actionDescriptor.MethodInfo.GetCustomAttribute<IgnoreRequiredValidationsAttribute>(false) != null;
}
#endregion
}
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class IgnoreRequiredValidationsAttribute : Attribute
{
}
}
Sources:
Using string debug = new StackTrace().ToString() to find out who is
handeling the model validation.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/configuring-aspnet-web-api to know how set my own validator.
https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http/Validation/DefaultBodyModelValidator.cs to figure out what this validator is doing.
https://github.com/Microsoft/referencesource/blob/master/System.Web/ModelBinding/DataAnnotationsModelValidator.cs to figure out why the IsRequired property is set on true. Here you can also find the original Attribute as a property.
If you don't want to use another ViewModel you can disable client validations on the view and also remove the validations on the server for those properties you want to ignore. Please check this answer for a deeper explanation https://stackoverflow.com/a/15248790/1128216
In my case the same Model was used in many pages for re-usability purposes. So what i did was i have created a custom attribute which checks for exclusions
public class ValidateAttribute : ActionFilterAttribute
{
public string Exclude { get; set; }
public string Base { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!string.IsNullOrWhiteSpace(this.Exclude))
{
string[] excludes = this.Exclude.Split(',');
foreach (var exclude in excludes)
{
actionContext.ModelState.Remove(Base + "." + exclude);
}
}
if (actionContext.ModelState.IsValid == false)
{
var mediaType = new MediaTypeHeaderValue("application/json");
var error = actionContext.ModelState;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, error.Keys, mediaType);
}
}
}
and in your controller
[Validate(Base= "person",Exclude ="Age,Name")]
public async Task<IHttpActionResult> Save(User person)
{
//do something
}
Say the Model is
public class User
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(18,99)]
public string Age { get; set; }
[MaxLength(250)]
public string Address { get; set; }
}
This one worked for me:
$('#fieldId').rules('remove', 'required');
Yes it is possible to disable Required Attribute. Create your own custom class attribute (sample code called ChangeableRequired) to extent from RequiredAtribute and add a Disabled Property and override the IsValid method to check if it is disbaled. Use reflection to set the disabled poperty, like so:
Custom Attribute:
namespace System.ComponentModel.DataAnnotations
{
public class ChangeableRequired : RequiredAttribute
{
public bool Disabled { get; set; }
public override bool IsValid(object value)
{
if (Disabled)
{
return true;
}
return base.IsValid(value);
}
}
}
Update you property to use your new custom Attribute:
class Forex
{
....
[ChangeableRequired]
public decimal? ExchangeRate {get;set;}
....
}
where you need to disable the property use reflection to set it:
Forex forex = new Forex();
// Get Property Descriptor from instance with the Property name
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(forex.GetType())["ExchangeRate"];
//Search for Attribute
ChangeableRequired attrib = (ChangeableRequired)descriptor.Attributes[typeof(ChangeableRequired)];
// Set Attribute to true to Disable
attrib.Disabled = true;
This feels nice and clean?
NB: The validation above will be disabled while your object instance is alive\active...

Categories