i am new in mvc .here scott shows how to Creating a Custom [Email] Validation Attribute in mvc. here is the picture.
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
1) now see how they did it. first create a class give a name and extend regular expression attribute class and in its ctor they use regex to validate email address
my question is when they use [Email(Errormessage="blah blah")]
then how MVC can understand this email attribute is pointing to email attribute class which extend regularexpression attribute class. how relation will be extanlish. the class name is email attribute but when they use then they use attribite name email. this is not clear to me please explain.
2) if i validate the email the above way they where validation will occur means at server side or client side ?
if not client side then how can i make it client and required js will be render for that.
please explain me with sample code example. thanks
The first question is best answered with a principle widely used in MVC: convention over configuration. That basically means: do the less config possible, use the most default functionalities. Several examples in ASP.NET MVC
Folder Controllers contain controllers by default.
The name of a view corresponds to the name of a Action in a Controller.
The folder name where a view is located corresponds to the Controller name without 'Controller' ending.
The class name of the controller ends with 'Controller' which is omitted when calling the controller.
The same with Attributes; the class name ends with 'Attribute' which is omitted in usage
etc, etc, etc,
There are many more like this and it is not configured. It is convention.
The second question is already partially answered in the question itself: you cannot inherit from EmailAddressAttribute as it's a sealed class. But you can use
RegularExpressionAttribute the way it's described in your question, or create a new attribute, like I will do it below.
However this way the validation will take place only on server side. To make it on client side you need to do the following:
public class EmailAttribute : ValidationAttribute, IClientValidatable
{
private const string VALIDATION_TYPE = "customEmail";
private const string EMAIL_REGEX = #"put your regex here";
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule { ValidationType = VALIDATION_TYPE, ErrorMessage = ErrorMessageString };
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var sValue = value as string;
if (!string.IsNullOrEmpty(sValue) && Regex.Match(sValue, EMAIL_REGEX).Success)
{
return ValidationResult.Success;
}
return new ValidationResult(string.Format(ErrorMessageString, validationContext.MemberName));
}
}
Then in Javascript (I suppose you've included jQuery, jQuery.validate and jQuery.validate.unobtrusive) use the following:
$.validator.addMethod('customEmail', function (value, element) {
let regex = /put your regex here/;
return regex.test($(element).val());
});
$.validator.unobtrusive.adapters.add('customEmail', [], function (options) {
options.messages['customEmail'] = options.message;
options.rules['customEmail'] = options.params;
});
Related
I am working on an ASP.NET MVC 3 project and need to exclude certain properties from server side validation. I am looking for something like in this question Is it possible to override the required attribute on a property in a model? but I do not want to bind the model again because I have already made changes to it (from my understanding that is what TryUpdateModel will do).
From related question
public ActionResult SomeAction()
{
var model = new BaseModel();
if (TryUpdateModel(model, null, null, new[] { "RequiredProperty" })) // fourth parameter is an array of properties (by name) that are excluded
{
// updated and validated correctly!
return View(model);
}
// failed validation
return View(model);
}
I want to validate the already updated model excluding certain properties. Should I just use TryUpdateModel like hackedbychinese suggested in the linked question? Could it change the values of the properties?
I ended up using the jquery validation rules remove method.
$("#fax_DisplayPhoneNumber").rules("remove", "required");
This overrides the [Required] tag on the fax DisplayPhoneNumber property in the model so it is no longer a required input.
I'm developing an MVC3 application and I have a page (well, a view) that let the users edit document's metainfo (a classic #Html.BeginForm usage). For general documents users will see standard fields to fill up, but through a dropdownlist they will be able to specify the type of the document: this, through an ajax call, will load new fields on the edit-document-form.
Whem the user submit the completed form, at last, the controller should read all the standard fields, plus all the fields loaded as being specific to the type of document selected.
Question is, how can I handle all this extra fields in a controller?
Say that I have Document class and a bunch of other classes extendinf Document, like Contract : Document, Invoice : Document, Complaint : Document and so forth, each having specific property (and this fields loaded on the form), how do I write the action in the controller?
I thought to use something like (I'll omitt all the conversions, validations, etc, for brevity)
[HttpPost]
public ActionResult Save(dynamic doc)
{
int docType = doc.type;
switch (docType)
{
case 1:
var invoice = new Invoice(doc);
invoice.amount = Request.Form["amount_field"];
invoice.code = Request.Form["code_field"];
//and so forth for every specific property of Invoice
Repository.Save(invoice);
break;
case 2:
var contract = new Contract(doc);
contract.fromDate = Request.Form["fromDate_field"];
contract.toDate = Request.Form["toDate_field"];
//and so forth for every specific property of Contract
Repository.Save(contract);
break;
..... // and so forth for any document types
default:
break;
}
}
But it seems a very dirty approach to me. Do you have a better idea on how to achive this? Maybe there's a pattern that I don't know nothing about to approach this kind of scenario.
Update
A second idea comes to my mind. After commenting Rob Kent's answer, I thought I could take a different approach, having just one class Document with a property like
public IEnumerable<Field> Tipologie { get; set; }
where
public class Field
{
public int IdField { get; set; }
public String Label { get; set; }
public String Value { get; set; }
public FieldType ValueType { get; set; }
public List<String> PossibleValues { get; set; } // needed for ENUMERATION type
}
public enum FieldType
{
STRING, INT, DECIMAL, DATE, ENUMERATION
}
Is this a better approach? In this case I can have just an action method like
[HttpPost]
public ActionResult Save(Document doc)
But shoud I create the fields in the view in order to make the MVC engine do the binding back to the model?
Given that the class inheriting from Document in the first approach will probably be generated at run-time, would you prefer this second approach?
To keep it all hard-typed on the server, you could use an abstract base type with a custom binder. See my answer here to see how this works: MVC generic ViewModel
The idea is that every time they load a new set of fields, you change the BindingType form variable to the instantiated type of the handler. The custom binder is responsible for creating the correct type on submission and you can then evaluate that in your action, eg:
if (model is Contract) ...
I'm not sure if you will be able to set up different actions each with a different signature, eg,:
public ActionResult Save(Contract contract) ...
public ActionResult Save(Invoice invoice) ...
Pretty sure that won't work because Mvc will have already decided which method to call, or maybe it will firstly see what type it gets back and then decides.
In my linked example, I am checking for overridden base members but if that is not an issue for you, you just need to create the correct type.
I am implementing ACL security using the spring-security-acl plugin. I have the following domain classes:
package test
class Subitem {
String name
static belongsTo = [employer: Employer]
static constraints = {
name blank: false
}
}
package test
class Employer {
String name
static hasMany = [users: User, items: Subitem]
static belongsTo = User
static constraints = {
name blank: false, unique: true
}
String toString() {
name
}
}
In the create.gsp file which is used to create a Subitem, there is the following statement:
<g:select id="employer" name="employer.id" from="${test.Employer.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
From the EmployerController:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
Following the tutorial given here, I have moved some of the functionality with dealing with Employer to a service called EmployerService:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, read)")
List<Employer> list(Map params) {
Employer.list params
}
int count() {
Employer.count()
}
Access to information in any given Employer class instance is restricted using ACL. At present, I can see ALL instances of Employer in the database in the drop down, and I assume that is because I am using the controller list(), not the service list() - however, I only want to see the filtered list of Employer domain classes. However, if I replace the g:select with:
<g:select id="employer" name="employer.id" from="${test.EmployerService.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
then I get an internal server error because I haven't passed a Map parameter to the service list() function (and I don't know how to do this within the tag):
URI /security/subitem/create
Class groovy.lang.MissingMethodException
Message No signature of method: static test.EmployerService.list() is applicable for argument types: () values: [] Possible solutions: list(java.util.Map), is(java.lang.Object), wait(), find(), wait(long), get(long)
I only want to see the information that comes from the EmployerService list() function - how do I do this please? How do I reference the correct function from within the gap?
Edit 16 Mar 0835: Thanks #OverZealous, that's really helpful, I hadn't realised that. However, I've tried that and still get the same problem. I've put a println() statement in both the Employer and EmployerService list() functions, and can see that neither actually seems to get called when the g:select tag is parsed (even if I leave the g:select to refer to Employer). Is there another version of the list() function that is being called perhaps? Or how else to get the g:select to take account of the ACL?
Just change your method signature in the Service to look like this:
List<Employer> list(Map params = [:]) {
Employer.list params
}
The change is adding this: = [:]. This provides a default value for params, in this case, an empty map.
(This is a Groovy feature, BTW. You can use it on any method or closure where the arguments are optional, and you want to provide a default.)
OK, I worked it out, and here is the solution to anyone else who comes up against the same problem.
The create Subitem page is rendered by means of the Subitem's create.gsp file and the SubitemController. The trick is to amend the SubitemController create() closure:
class SubitemController {
def employerService
def create() {
// this line was the default supplied method:
// [subitemInstance: new Subitem(params)]
// so replace with the following:
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[subitemInstance: new Subitem(params), employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
}
So now when the SubitemController is asked by the g:select within the Subitem view for the list of Employers, it calls the EmployerService, which supplies the correct answer. We have simply added 2 further variables that are returned to the view, and which can be referenced anywhere within the view (such as by the g:select tag).
The lesson for me is that the View interacts with the Controller, which can refer to a Service: the Service doesn't play nicely with a View, it seems.
ASP.NET MVC3/Razor newbie question:
I am setting up a model with custom validation. While the properties that I decorate with things like [Required] and [RegularExpression(...)] are performing as expected, I'm finding that the custom validation is not working. I made my model implement IValidatableObject, and I can hit a breakpoint inside the Validate() method and watch the method doing a yield return new ValidationResult(...); - but the form nonetheless gets posted.
Is there some secret switch that I'm missing?
If you are talking about server side validation, do you have the ModelState.Isvalid check?
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
The form will be posted when you use IValidatableObject to validate model properties. As Joeri Jans says, you can still prevent this and return the page to the user during your action method:
public ActionResult MyAction(MyModel model)
{
if (ModelState.IsValid)
{
// code to perform action when input is valid
return [return something]
}
return View(model); // re-display form because ModelState.IsValid == false
}
If you want your custom validation to prevent the form from being posted, you need to validate on the client. The easiest way to do this is with the RemoteAttribute.
public class MyModel
{
[Remote("MyValidateAction", "MyController", HttpMethod = "POST")]
public string MyProperty { get; set; }
}
You can still keep your code in IValidatableObject, and validate it from an action method like so:
[HttpPost]
public virtual JsonResult MyValidateAction(string myProperty)
{
var model = new MyModel{ MyProperty = myProperty, };
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(model,
new ValidationContext(model, null, null), results, true);
return isValid
? Json(true)
: Json(results[0].ErrorMessage);
}
The above action method does virtually the same thing as the default model binder. It constructs an instance of your viewmodel, then validates it. All validation rules will be checked, including your IValidatableObject code. If you need to send more properties to the action method for the construction of your viewmodel, you can do so with the AdditionalFields property of the RemoteAttribute.
Hope this helps.
I have an action method that takes in a string as its only parameter. The action method transforms it, and returns the result back to the client (this is done on an ajax call). I need to allow markup in the string value. In the past, I've done this by decorating the property on my model with [AllowHtml], but that attribute cannot be used on a parameter and the AllowHtmlAttribute class is sealed, so I cannot inherit from it. I currently have a work around where I've created a model with just one property and decorated it with the aforementioned attribute, and this is working.
I don't think I should have to jump through that hoop. Is there something I'm missing, or should I make a request to the MVC team to allow this attribute to be used on method parameters?
If you need to allow html input for a particular parameter (opposed to "model property") there's no built-in way to do that because [AllowHtml] only works with models. But you can easily achieve this using a custom model binder:
public ActionResult AddBlogPost(int id, [ModelBinder(typeof(AllowHtmlBinder))] string html)
{
//...
}
The AllowHtmlBinder code:
public class AllowHtmlBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
var name = bindingContext.ModelName;
return request.Unvalidated[name]; //magic happens here
}
}
Find the complete source code and the explanation in my blog post: https://www.jitbit.com/alexblog/273-aspnet-mvc-allowing-html-for-particular-action-parameters/
Have you looked at ValidateInputAttribute? More info here: http://blogs.msdn.com/b/marcinon/archive/2010/11/09/mvc3-granular-request-validation-update.aspx.