Show validation messages on GET - asp.net-mvc-3

We have a possibility that data loaded from a GET operation could be invalid for posting, and would like to be able to display the validation messages when the data is first loaded. The validation all takes place on server side using ValidationAttributes.
How can I force the validation summary to be displayed when the data is first loaded? I am guessing that I need to force errors into ModelState somehow, but I first need to get them out of the model class.

I ended up adding a validation method for the model class which adds errors to the ModelState. Then I created and added a custom ModelValidator and AssociatedValidatorProvider
for calling it during the normal validation that takes place during form binding. That way the controller actions that don't bind to the Model class directly can still have a call to the model's .Validate(ModelState) method to fake a validation. This approach works well for server-side-only validation.
UserInfo Model class:
private IEnumerable<RuleViolation> GetRuleViolations()
{
List<RuleViolation> violationList = new List<RuleViolation>();
if (String.IsNullOrWhiteSpace(FirstName))
violationList.Add(new RuleViolation("First Name is required.", FirstName"));
return violationList;
}
public void Validate(System.Web.Mvc.ModelStateDictionary ModelState)
{
foreach (RuleViolation violation in GetRuleViolations())
{
ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
}
}
This is how it can be used directly from a controller action. In this action the Model class object is returned as part of the UserSearch model.
public ActionResult Search(UserSearch model)
{
if (this.ModelState.IsValid)
{
model.Search();
if (model.UserInfo != null )
{
model.UserInfo.Validate(ModelState);
}
}...
That is all I had to do for the particular use case I was working on. But I went ahead and completed the work to do "normal" validation on a postback: created a simple ModelValidator, with the Validate override looking like this. If you followed the above pattern in all of your Model classes you could probably reusue this for them, too.
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var results = new List<ModelValidationResult>();
if (Metadata.Model != null)
{
UserInfoViewModel uinfo = Metadata.Model as UserInfoViewModel;
foreach (var violation in uinfo.GetRuleViolations())
{
results.Add(new ModelValidationResult
{
MemberName = violation.PropertyName,
Message = violation.ErrorMessage
});
}
}
return results;
}
Finally, extend AssociatedValidationProvider to return this ModelValidator and add it to the ModelValidationProviders collection in Application_Start. There is a writeup of this at http://dotnetslackers.com/articles/aspnet/Customizing-ASP-NET-MVC-2-Metadata-and-Validation.aspx#s2-validation

I don't know if understand what you need, but here is it...
run validation to display the validation summary when the form is loaded, using jquery
$(document).ready(function() {
$('#FormId').valid();
});

Related

Managing views and api from the same controller

I have an issue with my app, that is "hybrid", what I mean by "hybrid" controllers have to manage both views ans APIs.
So, basically, for each controller, I must check:
if $request->wantsJson(){
... // Client rendering using Angular, return json
}else{
// Server rendering Using blade, return view
}
I don't like the fact to have a conditional in every controller method.
I also wouldn't like to have a API folder with a copy of all my controller, there would be a lot of duplicated code.
How should I do it?
I would suggest to create a separate class to handle output ex: class ResultOutput with the method, output.
So, in your controller, when you are ready to output your data, just create a new instance of ResultOutput class, and call method output with relevant data.
In ResultOutput class, inject Request object so you can determine the method of the output based on above logic.
Ex: In your controller:
return (new ResultOutput())->output($data);
In ResultOutput class:
class ResultOutput()
{
private $type;
public __construct(Request $request) {
$this->output = 'view';
if ($request->wantsJson()) {
$this->output = 'json';
}
}
public method output($data) {
if ($this->type =='view') {
// return the view with data
} else {
// return the json output
}
}
}
In this way, if you need to introduce new output method (ex: xml), you can do it without changing all your controllers.

How do I mock validation in domain class for testing?

I have a domain class that uses a custom validator that's quite complex; it requires a separate collaborator. Now I'm creating a controller for it but I want to mock the part about validation. I want to be able to tell when a certain validation error occurs so that I control the behavior.
For example, I have,
class Person {
static constraint = {
key validator: {
//complex stuff goes here
}
}
}
In my controller spec, I want to be able to tell when I want the validation to pass or to fail. I tried doing Person.metaClass.validate = { true } but it doesn't seem to work. This is how my test code looks like.
void "Test controller when validation passes"() {
when:
controller.json = new Person(...)
controller.doSomething()
then:
//test post condition, etc.
}
If nothing works, I'm thinking of just mocking the command object directly, something like,
void "Test controller when validation passes"() {
when:
controller.doSomething(Mock(Person))
then:
1 * _.changeState(_)
//test post condition, etc.
}
but if I can get the first one working, I'd prefer it more.
I have similar tasks in my Project. Our implementation includes ErrorsContainer class that represents the concept of Mediator design pattern by encapsulating how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
Another advantage of this approach is that you can cache the result of the validation process. When applied to same preconditions, objects and rulles - it can improve the performance.
About the
I want to be able to tell when I want the validation to pass or to fail
you can just call it's isEmpty() and use it in simple code like this:
public class ValidationProcessor {
private ErrorsContainer errorsContainer;
//process Validation
........
//if validation errors are found add them
//into the container
........
//allow to manage the Validation process
public void SetValidationState(boolean isPassState){
if(isPassState){
//set it's state to 'pass'
errorsContainer.setEmpty();
}else{
//apply logic when validation errors are found
}
}
public void UseCurrentValidationState(){
if(errorsContainer.isEmpty()){
//apply logic when no validation errors are found
}else{
//apply logic when validation errors are found
}
}
}

Save data from a Complex View

I'm new to MVC and want to make sure I understand how to appreach a complex view I need to build.
I need to allow the user to add multiple Institutions on a page and each Institution can
have multiple trainings.
so the view model would be a list of institutions which contains a list of Trainings.
I will have a button that allows them to add multiple institutions and within the institution
section another button that will allow them to add multiple trainings.
On the postback method Do I simply loop through the institutions in the model
and for each institution loop through it's list of trainings to save them to the database?
You probably want to instead use a Controller like this:
public class InstitutionController : Controller
{
public ViewResult Index()
{
return View(); // Keep it simple, load data via JSON instead
}
[HttpPost]
public JsonResult Load()
{
// Go get Institutions etc
return Json(institutions);
}
[HttpPost]
public JsonResult Save(Institutions[] institutions)
{
try
{
// Save the institutions to the DB
}
catch (Exception ex)
{
return Json(new { Message = "Error." });
}
return Json(null); // Or some other way of saying it worked
}
}
That is, the Model you pass to the View is not persisted by the browser - instead it's briefly in server memory while the server generates a response.
In the example above however, you can create a View that uses for example jquery to load the Model from the server via JSON, which you can then persist in the browser for the life of the page. When the user makes a modification to an Institution or Instutions you can use a method like Save() above to send the new data or data changes to the server to be saved to the database.

How to disable validation before calling UpdateModel in MVC3 Controller

I'm looking to enable "saving" of form data prior to submission.
I want users to be able to save form progress, even when the form is in an invalid state, and then come back to it at a later time.
Now, my only problem is that I want to be able to use UpdateModel method to update my model. However, as the form is potentially invalid or just partially complete, this will throw an error.
Is there a way to annotate the following method such that validation is ignored in the SAVE instance?
[HttpPost]
public ActionResult Save(Model values)
{
var model = new Model();
UpdateModel(model);
}
I want to save having to write a 1-1 mapping for the elements being saved - which is my fallback option, but isn't very maintainable.
Give TryUpdateModel(model) a try, should fit your needs.
This won't throw an exception, and it will update the model and then return false if there are validation errors.
If you care about the errors, you check the ModelState in the false instance.
Hence, you can use it as so to always save changes:
[HttpPost]
public ActionResult Save(Model values)
{
var model = new Model();
TryUpdateModel(model);
model.saveChanges();
}

.NET MVC 3 Custom Controller Attribute

This may be pie in the sky but I'm wondering if the following could be accomplished with a custom controller attribute.
For a majority of my controllers, I will be passing in an URL parameter called "r" to each action within the controller. "r" is tied to a race id in the races table in my database.
What I would like to happen is that any time a controller action is invoked, it'll automatically check for the existence of "r", query the database to make sure "r" belongs to the logged in user and set a viewbag variable called ViewBag.RaceId equal to "r".
If any of those conditions aren't met, it'll redirect them back to the login page.
I'm trying to make my code as DRY as possible.
Any guidance would be greatly appreciated.
You could write a custom Authorize attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
var request = httpContext.Request;
// Fetch "r" from the route data or request
var r = request.RequestContext.RouteData.Values["r"]
?? request["r"];
var currentUser = httpContext.User.Identity.Name;
if (!CheckIfRBelongsToTheCurrentLoggedInUser(currentUser, r))
{
return false;
}
}
return isAuthorized;
}
}
Now all that's left is to decorate your controllers/actions with this custom attribute:
[MyAuthorize]
public ActionResult Foo()
{
//...
}
And if you wanted to put something into the ViewBag you could temporarily store it in the httpContext.Items inside the AuthorizeCore method in case of success and then override the OnAuthorization method as well and check for the presence of this item in the context. If it is present you could store it in the filterContext.Controller.ViewBag.

Resources