Remote validation doesn't work when property to validate is inside a modal Bootstrap - asp.net-mvc

I'm using ASP.NET MVC3.
When an user create an account, I need the chosen nickname be unique, so I use the Remote DataAnnotation like this :
public class UserModel
{
[Required]
[Remote("CheckNickname", "Validation", ErrorMessage = "This nickname is already used")]
public string Nickname { get; set; }
// ...
}
I used it in a strongly-typed view, via #Html.TextBoxFor(m => m.Nickname) and it perfeclty works.
However, I created another model with the exact same property.
public class MyOtherModel
{
// ...
[Required]
[Remote("CheckNickname", "Validation", ErrorMessage = "This nickname is already used")]
public string Nickname { get; set; }
}
I used this MyOtherModel.Nickname on a strongly-typed view via :
#Html.TextBoxFor(m => m.MyOtherModel.Nickname)
However, in this case only, the data passed to my CheckNickame() method is always null.
There are only two differences :
In the second case, the property I want to remotely validate is contained in another model (is it a problem ? I don't think so...)
In the second case, the property is displayed inside a modal bootstrap (is it a problem ?)
For information, this is what my CheckNickname() looks like :
public JsonResult CheckNickname(string nickname)
{
UserDAL userDAL = new UserDAL();
bool userIsAvailable = !userDAL.IsUserAlreadyInUse(nickname);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}
As I wrote it before, in the second case only, the parameter nickname is always null whereas it works as expected in the first case.
Is anyone knows why ?
Any help is appreciated.
UPDATE :
I created this method :
public JsonResult CheckNickname2([Bind(Prefix = "MyOtherModel")]string nickname)
{
UserDAL userDAL = new UserDAL();
bool userIsAvailable = !userDAL.IsUserAlreadyInUse(nickname);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}
The call is now :
http://mysite/Validation/CheckNickname2?MyOtherModel.Nickname=Alex
but if I put a breakpoint on CheckNickname2, the nickname paremeter is still null !
However, the call on the working validaton method is :
http://mysite/Validation/CheckNickname?Nickname=Alex
and this one works...
SOLUTION:
Ok, solved by changing [Bind(Prefix = "MyOtherModel")] to [Bind(Prefix = "MyOtherModel.Nickname")] as suggested by Stephen Muecke

In your second example, the html generated will be name="MyOtherModel.Nickname" so the key/value pair posted back will be MyOtherModel.Nickname:yourValue. Change the controller method to
public JsonResult CheckNickname([Bind(Prefix="MyOtherModel.Nickname")]string nickname)
which will effectively strip the prefix and bind correctly to parameter nickname
Note also that the modal usage may be a problem if this is adding dynamic content after the initial page has been rendered (in which case you need to re-parse the validator)

Related

Web.Api deserialization fail for model parameter with different name

I've got one method, which take a model [AccountLinkRequest] as a parameter with url-encoded data. It's uses Json.NET by default, and also, I can't use the setting UseDataContractJsonSerializer = true cause I have generic output response model (in other methods)
[HttpPost]
public SomeResponse Link(AccountLinkRequest request)
{
if (request.CustomerId == null)
throw new Exception("Deserialization error here, pls help!");
// other actions
}
Here is my model class:
[DataContract]
[JsonObject]
public class AlertAccountLinkRequest
{
[DataMember(Name = "id")]
public string id { get; set; }
[DataMember(Name = "customer_id")]
[JsonProperty("customer_id")]
public string CustomerId { get; set; }
}
The problem: request.CustomerId is allways null. The request is pretty simple:
web_service_URL/link?customer_id=customer_id&id=id (url-encoded)
if I use Customer_Id instead of CustomerId, everything will be fine, but I'm on a jedy-way. Thank you!
There is not a simple answer how to achieve that. For more details please read this:
How to bind to custom objects in action signatures in MVC/WebAPI
Summary:
Manually call the parse function inside of your Action
Use a TypeConverter to make the complex type be simple
Use a custom model binder
So, if you for instance create your 'SmartBinder' which is able to consume some attributes, you can get what you want. Out fo the box there is no functionality for that, just the naming conventions...

Entity Framework ASP.NET MVC private model fields

There is a field in our database which really ought to be a boolean, but for some reason the original developers made it a CHAR which will either be set to "1" or "0".
[Column("CHARGEABLE")]
[StringLength(1)]
private string Chargeable { get; set; }
I want my model to represent this field as a boolean so I figured I could add a property to my model to wrap it:
[NotMapped]
public bool ChargeableTrue
{
get
{
return Chargeable == "1" ? true : false;
}
set
{
Chargeable = value ? "1" : "0";
}
}
Now on my View I just display the EditorFor ( ChargeableTrue ), but when I click save it doesn't actually update it.
I think what is happening is that when the model is being updated, it's still attempting to get the value of 'Chargeable' from the View, even though I haven't displayed it there. And since there is no input field, it just gets null and ends up saving that to the database.
if (ModelState.IsValid)
{
db.Entry(call).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
What is one expected to do in this situation?
Based on KMan's answer, here's the extended version just in case you're not familiar with creating view models.
The idea is that your domain object is not really what you want to be updating exactly from your views. Instead, you create a go-between that can also include view-specific items (like a list of objects to populate a drop-down).
public class MyViewModel {
public bool Chargeable { get; set; }
}
Now you can do this:
#* In view *#
Html.EditorFor(m => m.Chargeable)
// In controller
public ActionResult Save(MyViewModel model) {
if (ModelState.IsValid) {
var domainObject = new MyObject() {
Chargeable = model.Chargeable ? "1" : "0"
};
// the rest of your code using domainObject
}
}
I'd consider just creating an overload of your domain object's constructor that accepts your view model to keep the mapping in one place. I typically use a tool like AutoMapper to map objects or manual extension methods.
A view model typically contains a sub-set of your domain object's properties, but can contain all of them or more properties like lists, visbility states, etc. They come in incredibly useful and I've never done a MVC project where I haven't used them.
Use a view model and make your mapping on the controller.

In MVC, why does the routeValues property in RedirectToAction() not accept my class as argument?

So here's the deal, i want to be able to export any Enumerable of items to excel:
Here's an ActionMethod in some Area of my app that constructs an "ExportToExcel" model, then Redirects it to an Action Method in another controller and another are which does all the formatting-to-excel work:
public ActionResult ExportCustomListToExcel()
{
var exportModel = new ExportToExcelModel();
//Here I fill up the model with a dataTable / other file info like
//exportModel.Items = blah blah..
return RedirectToAction("ExportToExcel", "Shared", new { model = exportModel, testString = "test", area = "Shared" });
}
And here's my Shared ExportToExcel ActionMethod:
public ActionResult ExportToExcel(ExportToExcelModel model, string testString)
{
//PROBLEM IS RIGHT HERE!
// where testString == "test"
// but model == null :(
//Ommited unrelated code
}
My ExportToExcel actionMethod gets hit, but somewhere along the way my ExportToExcelModel gets lost :(
Note: It succeeds on passing strings like "testString" so is there somwthing wrong with my model?
Just in case, the ExportToExcelModel is:
public class ExportToExcelModel
{
public ExportToExcelModel() {}
public ExportToExcelModel(string fileName, ItemType itemType, IEnumerable<ExportableToExcelItem> items)
{
this.FileName = fileName;
this.ItemType = ItemType;
this.Items = items;
}
public string FileName { get; set; }
public ItemType ItemType { get; set; }
public IEnumerable<ExportableToExcelItem> Items { get; set; }
}
Thanks in advance!
First time i've ever needed to actually ask a question here since every other question i've ever had i've found already answered here :)
EDIT: Posting FormCollection results:
http://imageshack.us/photo/my-images/861/sinttulonsa.png
Sorry, newbies cant post pics :(
The reason is that a RedirectToAction result will launch a GET request and your parameters will have to be passed along through the querystring. Obviously there is a limit to the amount of characters a url can consist of.
Seems to me that you should do the conversion to Excel in a class instead of behind another Action.
So CustomExportAction1 and CustomExportAction2 both call
return File(ExcelExporter.ExportExcel(dataToExport));
or something similar.
try to switch your ExportToExcel signature to
public ActionResult ExportToExcel(FormCollection data)
{
var model = new ExportToExcelModel();
try
{
UpdateModel(model, data)
}
catch(UpdateModelException ex)
{
}
}
look at what's in the FormCollection (that might help), and also see if UpdateModel is throwing an exception, because this is what is happening behind the seen when you make your action method take in a model instead of a FormCollection.
Hope that help you track it down
UPDATE:
You might have to do it using TempData, read this, supposedly you can't do this out of the box with ASP.NET MVC!!

ASP.NET MVC Validation Help

I'm doing some simple validation inside my Controller I know this would better placed inside something like a service layer but for this I want to keep it inside the Controller
The idea is to check that a valid url is being entered into a url field to display an image, e.g. http://domain.com/myawesomeimage.png would be valid and http://domain.com/ would not be valid.
// ValidateInt has a default value of 0
int ValidateInt = 0;
// If the url entered (if one at all) does not have correct extension then increment the ValidateInt
if (!ArticleToEdit.image.Contains(".jpg"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".jpeg"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".png"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".gif"))
ValidateInt++;
if (!ArticleToEdit.image.Contains(".bmp"))
ValidateInt++;
// if ValidateInt is bigger than 0 then the url is invalid
if (ValidateInt > 0)
ModelState.AddModelError("Image", "Please enter a valid URL.");
EDITED CODE
The problem with your code
Your code is invalid, because your model state will always have at least 4 errors. Even though the URL would be correct. Your code requires that your URL must have all extensions which is of course incorrect. It can only have one. At most.
The solution
Use DataAnnotations instead and use regular expression validator. You're obviously already using some application model class called ArticleToCreate. You'll have to put data annotations attribute on the image property (one more observation: keey property names with Pascal casing so it's the same as .net):
public class ArticleToCreate
{
[RegularExpression(#"...")] // add regular expression that fulfils your requirements
public string Image { get; set; }
...
}
Then it all depends how complicated your regular expression is. The easiest one for your needs could be just that it starts with an http:// and end with the correct extension:
^http:\/\/.+\.(?:png|jpe?g|gif|bmp)$
And if you're directly providing your class instance to controller action it will get automatically validated for you without any additional code. This way you won't be able to forget to validate your objects manually.
Consider this controller action that automatically validates your model class object instance by validators defined on it (as per validator definition I've written above):
public ActionResult Create(ArticleToCreate data)
{
if (!this.ModelState.IsValid)
{
// handle invalid object
}
// handle valid object
}
This way your actions will focus on the processing part which is their main objective instead of focusing on too many aspects of your business process like validation for instance.
Shorter code = simpler code = easier to maintain = less bugs = less work = happy clients
Why not create custom ValidationAttributes (from DataAnnotations) and allow the Validation Engine do the work for you rather than worrying about where to put your logic?
I'm guessing it would look something like:
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field, AllowMultiple = false)]
public class ValidImageUrlAttribute : ValidationAttribute
{
public string ErrorMessage { get; set; }
public override bool IsValid(object value)
{
var url = value as string;
if(!url.Contains(".jpg") || !url.Contains(".jpeg")
|| !url.Contains(".gif") || !url.Contains(".bmp")
|| !url.Contains(".png"))
{
return false;
}
return true;
}
public override string FormatErrorMessage(string name)
{
return ErrorMessage ?? base.FormatErrorMessage(name);
}
}
And then you could decorate your Model:
[Required(ErrorMessage = "Image URL is required.")]
[ValidImageUrl(ErrorMessage = "Valid Image URL is required.")]
public string ImageUrl { get; set; }
changed if (validate1 > 0) and it works fine :)

ASP.NET MVC - Custom validation message for value types

When I use UpdateModel or TryUpdateModel, the MVC framework is smart enough to know if you are trying to pass in a null into a value type (e.g. the user forgets to fill out the required Birth Day field) .
Unfortunately, I don't know how to override the default message, "A value is required." in the summary into something more meaningful ("Please enter in your Birth Day").
There has to be a way of doing this (without writing too much work-around code), but I can't find it. Any help?
EDIT
Also, I guess this would also be an issue for invalid conversions, e.g. BirthDay = "Hello".
Make your own ModelBinder by extending DefaultModelBinder:
public class LocalizationModelBinder : DefaultModelBinder
Override SetProperty:
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
foreach (var error in bindingContext.ModelState[propertyDescriptor.Name].Errors.
Where(e => IsFormatException(e.Exception)))
{
if (propertyDescriptor.Attributes[typeof(TypeErrorMessageAttribute)] != null)
{
string errorMessage =
((TypeErrorMessageAttribute)propertyDescriptor.Attributes[typeof(TypeErrorMessageAttribute)]).GetErrorMessage();
bindingContext.ModelState[propertyDescriptor.Name].Errors.Remove(error);
bindingContext.ModelState[propertyDescriptor.Name].Errors.Add(errorMessage);
break;
}
}
Add the function bool IsFormatException(Exception e) to check if an Exception is a FormatException:
if (e == null)
return false;
else if (e is FormatException)
return true;
else
return IsFormatException(e.InnerException);
Create an Attribute class:
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]
public class TypeErrorMessageAttribute : Attribute
{
public string ErrorMessage { get; set; }
public string ErrorMessageResourceName { get; set; }
public Type ErrorMessageResourceType { get; set; }
public TypeErrorMessageAttribute()
{
}
public string GetErrorMessage()
{
PropertyInfo prop = ErrorMessageResourceType.GetProperty(ErrorMessageResourceName);
return prop.GetValue(null, null).ToString();
}
}
Add the attribute to the property you wish to validate:
[TypeErrorMessage(ErrorMessageResourceName = "IsGoodType", ErrorMessageResourceType = typeof(AddLang))]
public bool IsGood { get; set; }
AddLang is a resx file and IsGoodType is the name of the resource.
And finally add this into Global.asax.cs Application_Start:
ModelBinders.Binders.DefaultBinder = new LocalizationModelBinder();
Cheers!
With the DefaultModelBinder it is possible to override the default required error message but unfortunately it would apply globally which IMHO renders it completely useless. But in case you decide to do it here's how:
Add the App_GlobalResources folder to your ASP.NET site
Add a resources file called Messages.resx
Inside the resources file declare a new string resource with the key PropertyValueRequired and some value
In Application_Start add the following line:
DefaultModelBinder.ResourceClassKey = "Messages";
As you can see there's no link between the model property you are validating and the error message.
In conclusion it is better to write custom validation logic to handle this scenario. One way would be to use a nullable type (System.Nullable<TValueType>) and then:
if (model.MyProperty == null ||
/** Haven't tested if this condition is necessary **/
!model.MyProperty.HasValue)
{
ModelState.AddModelError("MyProperty", "MyProperty is required");
}
I've been using the awesome xVal validation framework. It lets me do all my validation in the model (Even LINQ-SQL :)). It also emits the javascript required for client side validation.
EDIT: Sorry left out the link for how to get it working for LINQ-SQL
The basic workflow goes something like this.
public partial class YourClass
{
[Required(ErrorMessage = "Property is required.")]
[StringLength(200)]
public string SomeProperty{ get; set; }
}
try
{
// Validate the instance of your object
var obj = new YourClass() { SomeProperty = "" }
var errors = DataAnnotationsValidationRunner.GetErrors(obj);
// Do some more stuff e.g. Insert into database
}
catch (RulesException ex)
{
// e.g. control name 'Prefix.Title'
ex.AddModelStateErrors(ModelState, "Prefix");
ModelState.SetModelValue("Prefix.Title", new ValueProviderResult(ValueProvider["Prefix.Title"].AttemptedValue, collection["Prefix.Title"], System.Globalization.CultureInfo.CurrentCulture));
}
how about this?
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$",
ErrorMessage = "Characters are not allowed.")]
That should allow you to tag properties with specific error messages for whatever MVC validators you want to use...
In ASP.NET MVC 1, I met this problem too.
In my project, there is a model or business object named "Entry", and its primary key EntryId is int? type, and the value of EntryId can be allowd to input by users.
So the problem is, when the field is blank or zero or some integer value that has existed, the custom error messages can be shown well, but if the value is some non-integer value like "a", i can not find a way to use the custom message to replace the default message like "The value 'a' is invalid".
when i track the error message in ModelState, i found when the value is non-integer, there will be two errors related to EntryId, and the first item's error message is blank...
Now i have to use such an ugly code to hack the problem.
if (ModelState["EntryId"].Errors.Count > 1)
{
ModelState["EntryId"].Errors.Clear(); //should not use ModelState["EntryId"].remove();
ModelState.AddModelError("EntryId", "必须为大于0的整数"); //必须为大于0的整数 means "it should be an integer value and great than 0"
}
but this makes controller fat, hope there is a real solution to solve it.
Look up ModelState.AddError.
yes, there is a way, you must use System.ComponentModel.DataAnnotations in combination with xVal and you are going to be able to set validation rules and messages (u can even use resource files for localization) for each of your property using Attributes
look here http://blog.codeville.net/2009/01/10/xval-a-validation-framework-for-aspnet-mvc/

Resources