Custom error for view-model int overflow (invalid values) - asp.net

In my model I have an nullable int.
In the view I have an input of type 'number'.
The problem is that the user could possibly input a number larger than the maximum possible integer value, which results in the following error:
The value [value] is not valid for [field]
I want to change this message, and putting a [Range(...)] validation on the attribute does not help, as non of my validations are being called, because the framework cannot parse the value to an int.
--------- EDIT ---------
My validations perform normally at normal input as shown here:
What I mean about my validations not being called is that I've read that the if the framework fails to parse the value to an int (because the value exceeds the max value of an integer), it ignores any further "unnecessary" validation.
Therefore my Range validation is ignored, as shown here:
I've tried creating a custom ValidationAttribute, but that has the same behaviour (Being called correctly at normal integer input, but is ignored at overflowing integer input)
--------- /EDIT ---------
All the solutions I have seen, proposes binding a custom resource file, in which I could override the default message for invalid values.
But I want to be able to specify a custom message at each attribute in case of an invalid value.
I thought about using strings instead of ints, and creating a custom ValidationAttribute which tries to parse it to an int.
Are there any alternatives?
// EDIT: I could avoid this problem (to some degree) by having client-side validation - But I want to know if it is possible from the server-side, in case of someone editing the html and forcing a higher value

There is couple of ways in which you can validate user input:
proper usage of Data Annotations - you could take a look here: http://www.asp.net/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-6 and here: https://msdn.microsoft.com/en-us/library/ee256141(v=vs.100).aspx
usage of jQuery Validation - http://jqueryvalidation.org/
Here is an example of custom validation attribute:
public class MyIntegerValidationAttribute : ValidationAttribute
{
public string[] PropertyNames { get; }
public MyIntegerValidationAttribute(params string[] propertyNames)
{
PropertyNames = propertyNames;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var properties = this.PropertyNames.Select(validationContext.ObjectType.GetProperty);
//here you have values of your properties
var values = properties.Select(p => p.GetValue(validationContext.ObjectInstance, null)).OfType<int>();
if (YOUR_CUSTOM_CONDITION)
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
public class ViewModel
{
[MyIntegerValidationAttribute("B", "C", ErrorMessage = "My error message")]
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}

Related

Asp.Net Core Model binding, how to get empty field to bind as a blank string?

If a form like the one below is submitted and MyField is left blank on the form, then by default Asp.Net Core model binding will place null into the corresponding property on the model as indicated below.
Example Form
<form asp-controller="SomeController" asp-action="SomeAction">
<label asp-for="MyField">My Field</label><input asp-for="MyField" type="text" />
<button type="submit">Submit</button>
</form>
Example Model
public class MyModel{
public string MyField { get; set; }
}
Example Action Method
[HttpPost]
public IActionResult Post(MyModel m) {
//m.MyField will be null if the field was left empty
//but I want it set to a blank string by the model binder
}
However, since MyField is actually transmitted in the Http Post body I'd prefer that the model binder set the MyField property on the model to a blank string rather than setting it to null. I'd prefer to reserve null for cases where MyField is not transmitted in the Http Post body. How can the model binder be changed to exhibit this behavior?
Studying the ASP.NET Core code for SimpleTypeModelBinder at https://github.com/aspnet/Mvc/blob/rel/1.1.3/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Binders/SimpleTypeModelBinder.cs I could see that there is a ModelMetadata.ConvertEmptyStringToNull setting which is set to true by default that is causing the blank string to be converted to null on data binding. But the property is read only so at first I couldn't figure out how to changes its value.
#rsheptolut's post on this page https://github.com/aspnet/Mvc/issues/4988 led me to a solution.
Solution:
The value has to get set at startup. This can be done via this class:
public class CustomMetadataProvider : IMetadataDetailsProvider, IDisplayMetadataProvider {
public void CreateDisplayMetadata(DisplayMetadataProviderContext context) {
if (context.Key.MetadataKind == ModelMetadataKind.Property) {
context.DisplayMetadata.ConvertEmptyStringToNull = false;
}
}
}
When it's hooked into MvcOptions in the ConfigureServices method of the startup.cs file like so
services.AddMvc()
.AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add(new CustomMetadataProvider ()));
Now site wide, the default for a blank field that is posted back will be for the data binder to set the corresponding model property to a blank string rather than to null. Yea!
Have you tried making the property have a default value of empty string?
public string MyField { get; set; } = string.Empty;
an uglier solution to try is:
private string myField = string.Empty;
public string MyField
{
get { return myField ?? string.Empty; }
set { myField = value; }
}
I think it should work

How do I get the strongly-typed value of an OutArgument in code?

Given an Activity (created via the designer) that has several OutArgument properties, is it possible to get their strongly-typed value from a property after invoking the workflow?
The code looks like this:
// generated class
public partial class ActivityFoo : System.Activities.Activity....
{
....
public System.Activities.OutArgument<decimal> Bar { ... }
public System.Activities.OutArgument<string> Baz { ... }
}
// my class
var activity = new ActivityFoo();
var result = WorkflowInvoker.Invoke(activity);
decimal d = activity.Bar.Get(?)
string s = activity.Baz.Get(?)
The T Get() method on OutArgument<T> that requires an ActivityContext which I'm not sure how to obtain in code.
I also realize it's possible to get the un-typed values from result["Bar"] and result["Baz"] and cast them, but I'm hoping there's another way.
Updated to make it clear there are multiple Out values, although the question would still apply even if there was only one.
If you look at workflows as code, an Activity is no more than a method that receives input arguments and (potentially) returns output arguments.
It happens that Activities allows one to return multiple output arguments, something that C# methods, for example, don't (actually that's about to change with C# 7 and tuples).
That's why you've an WorkflowInvoker.Invoke() overload which returns a Dictionary<string, object> because the framework obviously doesn't know what\how many\of what type output arguments you have.
Bottom line, the only way for you to do it fully strong-typed is exactly the same way you would be doing on a normal C# method - return one OutArgument of a custom type:
public class ActivityFooOutput
{
public decimal Bar { get; set }
public decimal Baz { get; set; }
}
// generated class
public partial class ActivityFoo : System.Activities.Activity....
{
public System.Activities.OutArgument<ActivityFooOutput> Result { ... }
}
// everything's strongly-typed from here on
var result = WorkflowInvoker.Invoke<ActivityFooOutput>(activity);
decimal d = result.Bar;
string s result.Baz;
Actually, if you don't want to create a custom type for it, you can use said tuples:
// generated
public System.Activities.OutArgument<Tuple<decimal, string>> Result { ... }
// everything's strongly-typed from here on
var result = WorkflowInvoker.Invoke<Tuple<decimal, string>>(activity);
decimal d = result.Item1;
string s result.Item2;
Being the first option obviously more scalable and verbose.

FromUri binding when querystring is empty

If we have the following controller action in Web API
public async Task<HttpResponseMessage> GetRoutes(
[FromUri] MapExtentQuery extent,
[FromUri] PagingQuery paging)
{
...
}
with
class MapExtentQuery {
public int X { get;set; }
public int Y { get; set; }
}
class PagingQuery {
public int Skip { get; set; }
public int Top { get; set; }
}
and we make a GET request to /routes both parameters (extent and paging) will be null.
If the request contains at least one querystring parameter, for instance
/routes?x=45
then both complex parameters will get initialized, so in the case of the 2nd route
extent.X = 45
extent.Y = 0
paging != null (but Skip and Top will be 0 of course).
Why does the [FromUri] binder work this way? It makes little or no sense.
I would understand if it initialized only the parameter that contains a property that matched at least one of the querystring values.
The problem is, this behaviour requires us to check when parameters are null (which happens only in the case that no querystring parameter was provided) and then initiliaze them ourselves.
Because obviously those complex params might have constructors which would set some property values to default.
It is because when you do not have query string and the parameters have FromUri attribute, the parser for the query string does not run and all values are not binded - you receive null.
If you have query string, the binder runs and instantiates all FromUri parameters and primitive types with their default values and then tries to fill in the properties from the query string values. This leaves you with two instantiated parameters but only one having the populated value because that is all the query string has.
As for why it works this way - most likely performance - this way they will not need to find which parameters they need to instantiate - they instantiate all of them and then find the properties. It is faster and performance is critical in the action and model binding.

Why is IValidatableObject.Validate only called if property validation passes?

In my model, it seems that Validate() is only called AFTER both properties pass validation.
public class MyModel : IValidatableObject
{
[Required]
public string Name { get; set;}
[Required]
public string Nicknames {get; set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(Nicknames != null && Nicknames.Split(Environment.NewLine.ToCharArray()).Count() < 2)
return yield result new ValidationResult("Enter at least two nicknames, new [] { "Nicknames" });
}
}
When a user enters a single line of text in the Nicknames text area but leaves the Name text box empty, only the Required error message for the Name property is displayed. The error message that should be displayed from the Validate() function never shows up.
Only after entering a name in the Name text box and some text in the Nicknames text is the Validate() function called.
Is this how it's supposed to work? It seems odd that a user is shown an error message on a subsequent page when the error is being caused on the current page.
This is by design. Object-level validation does not fire until all the properties pass validation because otherwise it is possible that the object is incomplete. The Validate method is meant for thing like comparing one property to another. In your case you should write a custom property validator.

ASP.NET MVC - Posting a form with custom fields of different data types

In my ASP.NET MVC 2 web application, I allow users to create custom input fields of different data types to extend our basic input form. While tricky, building the input form from a collection of custom fields is straight-forward enough.
However, I'm now to the point where I want to handle the posting of this form and I'm not certain what the best way to handle this would be. Normally, we'd use strongly-typed input models that get bound from the various statically-typed inputs available on the form. However, I'm at a loss for how to do this with a variable number of input fields that represent different data types.
A representative input form might look something like:
My date field: [ date time input
control ]
My text field: [ text input
field ]
My file field: [ file upload
control ]
My number field: [ numerical input control ]
My text field 2: [text input field ]
etc...
Ideas I've thought about are:
Sending everything as strings (except for the file inputs, which would need to be handled specially).
Using a model with an "object" property and attempting to bind to that (if this is even possible).
Sending a json request to my controller with the data encoded properly and attempting to parse that.
Manually processing the form collection in my controller post action - certainly an option, but I'd love to avoid this.
Has anyone tackled an issue like this before? If so, how did you solve it?
Update:
My "base" form is handled on another input area all together, so a solution doesn't need to account for any sort of inheritence magic for this. I'm just interested in handling the custom fields on this interface, not my "base" ones.
Update 2:
Thank you to ARM and smartcaveman; both of you provided good guidance for how this could be done. I will update this question with my final solution once its been implemented.
This is how I would begin to approach the issue. A custom model binder would be pretty easy to build based on the FormKey property (which could be determined by the index and/or label, depending).
public class CustomFormModel
{
public string FormId { get; set; }
public string Label { get; set; }
public CustomFieldModel[] Fields { get; set; }
}
public class CustomFieldModel
{
public DataType DateType { get; set; } // System.ComponentModel.DataAnnotations
public string FormKey { get; set; }
public string Label { get; set; }
public object Value { get; set; }
}
public class CustomFieldModel<T> : CustomFieldModel
{
public new T Value { get; set; }
}
Also, I noticed one of the comments below had a filtered model binder system. Jimmy Bogard from Automapper made a really helpful post about this method at http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx , and later revised in, http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx . It has been very helpful for me in building custom model binders.
Update
I realized that I misinterpreted the question, and that he was specifically asking how to handle posting of the form "with a variable number of input fields that represent different data types". I think the best way to do this is to use a structure similar to above but leverage the Composite Pattern. Basically, you will need to create an interface like IFormComponent and implement it for each datatype that would be represented. I wrote and commented an example interface to help explain how this would be accomplished:
public interface IFormComponent
{
// the id on the html form field. In the case of a composite Id, that doesn't have a corresponding
// field you should still use something consistent, since it will be helpful for model binding
// (For example, a CompositeDateField appearing as the third field in the form should have an id
// something like "frmId_3_date" and its child fields would be "frmId_3_date_day", "frmId_3_date_month",
// and "frmId_3_date_year".
string FieldId { get; }
// the human readable field label
string Label { get; }
// some functionality may require knowledge of the
// Parent component. For example, a DayField with a value of "30"
// would need to ask its Parent, a CompositeDateField
// for its MonthField's value in order to validate
// that the month is not "February"
IFormComponent Parent { get; }
// Gets any child components or null if the
// component is a leaf component (has no children).
IList<IFormComponent> GetChildren();
// For leaf components, this method should accept the AttemptedValue from the value provider
// during Model Binding, and create the appropriate value.
// For composites, the input should be delimited in someway, and this method should parse the
// string to create the child components.
void BindTo(string value);
// This method should parse the Children or Underlying value to the
// default used by your business models. (e.g. a CompositeDateField would
// return a DateTime. You can get type safety by creating a FormComponent<TValue>
// which would help to avoid issues in binding.
object GetValue();
// This method would render the field to the http response stream.
// This makes it easy to render the forms simply by looping through
// the array. Implementations could extend this for using an injected
// formatting
void Render(TextWriter writer);
}
I am assuming that the custom forms can be accessed via some sort of id which can be contained as a form parameter. With that assumption, the model binder and provider could look something like this.
public interface IForm : IFormComponent
{
Guid FormId { get; }
void Add(IFormComponent component);
}
public interface IFormRepository
{
IForm GetForm(Guid id);
}
public class CustomFormModelBinder : IModelBinder
{
private readonly IFormRepository _repository;
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult result;
if(bindingContext.ValueProvider.TryGetValue("_customFormId", out result))
{
var form = _repository.GetForm(new Guid(result.AttemptedValue));
var fields = form.GetChildren();
// loop through the fields and bind their values
return form;
}
throw new Exception("Form ID not found.");
}
}
Obviously, all the code here is just to get the point across, and would need to be completed and cleaned up for actual use. Also, even if completed this would only bind to an implementation of the IForm interface, not a strongly typed business object. (It wouldn't be a huge step to convert it to a dictionary and build a strongly typed proxy using the Castle DictionaryAdapter, but since your users are dynamically creating the forms on the site, there is probably no strongly typed model in your solution and this is irrelevant). Hope this helps more.
Take a peek at what I did here: MVC2 Action to handle multiple models and see if can get you on the right track.
If you use a FormCollection as one of your parameters to your action, you can then go thru that form collection looking for bits of data here or there in order to bind those values to whatever an then save the data. You are most likely going to need to take advantage of both strategy and command patterns to get this to work.
Best of luck, feel free to ask follow-up questions.
Edit:
Your method which does the work should look something like this:
private/public void SaveCustomFields(var formId, FormCollection collection) //var as I don't know what type you are using to Id the form.
{
var binders = this.binders.select(b => b.CanHandle(collection)); //I used IOC to get my list of IBinder objects
// Method 1:
binders.ForEach(b => b.Save(formId, collection)); //This is the execution implementation.
// Method 2:
var commands = binders.Select(b => b.Command(formId, collection));
commands.ForEach(c => c.Execute());
}
public DateBinder : IBinder //Example binder
{
public bool CanHandle(FormCollection collection)
{
return (null != collection["MyDateField"]); //Whatever the name of this field is.
}
//Method 1
public void Save(var formId, FormCollection collection)
{
var value = DateTime.Parse(collection["MyDateField"]);
this.someLogic.Save(formId, value); //Save the value with the formId, or however you wish to save it.
}
//Method 2
public Command Command(var formId, FormCollection collection)
{
//I haven't done command pattern before so I'm not sure exactly what to do here.
//Sorry that I can't help further than that.
}
}
I would think one of the best options is to create a custom model binder, which makes it possible to have custom logic behind the scenes and still very customizable code behind.
Maybe these articles can help you:
http://www.gregshackles.com/2010/03/templated-helpers-and-custom-model-binders-in-asp-net-mvc-2/
http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
More specifically I would probably take as the controller argument a custom class with all "base" properties included. The class could then for example include a dictionary linking the name of each field to either just an object or an interface which you implement once for each data-type making it simple to process the data later.
/Victor

Resources