scott.korin
scott.korin

Reputation: 2597

Force server-side validation when jquery unobtrusive client side validation fails with ASP.NET MVC3

I will show some minimum code, since I don't think my company wants me showing a whole lot, although we are currently just doing research.

We are using POCO in Entity Framework 4 to save data back to a database. We are using data annotations to try to reduce the amount of duplicate validation we have to do (which was a problem with our old classic ASP solution, where we had the SAME validation occurring on three different levels of the application).

We want to include business rules in our model, which includes making sure that fields that are validated against other tables are valid (we don't use dropdowns in our model, so users can type in anything). For example, we are storing room information in a building. The room has a field called "Room Type". Valid room types are defined in a different table.

We also want to have immediate client-side validation. For example, we have number fields that have to be between 0 and 32767. If the user types in -1, we use client side validation to immediately respond to the user to let them know -1 is invalid. We do that by turning on client side validation and using data annotations.

Web Config:

<appSettings>
    <add key="webpages:Version" value="1.0.0.0" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

Model:

public class Room : IValidatableObject
{
    // I'm only showing the relevant properties...

    // this is a field that's validated based on another table in the database.
    // (In the model we are using autocomplete instead of a dropdown -- it's a long 
    // story --, so potentially invalid data can be passed through the form...
    [DisplayName("Room Type"), MaxLength(5)]        
    public String RoomType { get; set; }

    // A number field, with a range.
    [Range(0, 32767), DisplayName("Maximum Seats")]
    public Int16? MaxStudents { get; set; }

    // do server side validation for this entity.
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var validateErrors = new List<ValidationResult>();

        // make sure room type is a valid type
        if(!String.IsNullOrEmpty(RoomType)) {
            // hit database, which is a little weird since we need to create a context,
            // and I know that might make some people's brains explode
        }

        // return all of the errors
        return validateErrors;
    }
}

Controller:

    // I didn't include all the actions, just edit

    public ActionResult Edit(Int32 Building, String RoomID)
    {
        // return a single room and show the detail information in edit mode.
        Room room = findRoom(Building, RoomID);
        return View(room);
    }

    [HttpPost]
    public ActionResult Edit(Int32 Building, String RoomID, FormCollection collection)
    {
        // get the current room from the database.
        Room room = findRoom(Building, RoomID);

        // save the room being edited, but don't touch the key fields.
        if (TryUpdateModel(room, null, null, new string[] {"Building", "RoomID"}))
        {
            // if the entity changed, audit and save
            if (db.Entry(room).State == EntityState.Modified)
            {
                db.setLastUpdate(room); // this is a function in our context for auditing
                db.SaveChanges();
            }                
        }
        return View(room);
    }

View: (not bothering to show the javascript used to create the autocomplete...)

@using (Html.BeginForm()) {
    @Html.ValidationSummary(false, "The following errors occured when attempting to save this Room:")
    <fieldset>
        <legend>Room</legend>
        <div class="field-block">
            <div class="editor-label">
                @Html.LabelFor(model => model.Building)
            </div>
            <div class="editor-field">
                @Html.DisplayFor(model => model.Building)
            </div>
        </div>
        <div class="field-block">
            <div class="editor-label">
                @Html.LabelFor(model => model.RoomID)
            </div>
            <div class="editor-field">
                @Html.DisplayFor(model => model.RoomID)
            </div>
        </div>
        <div class="field-block">
            <div class="editor-label">
                @Html.LabelFor(model => model.RoomType)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.RoomType)
            </div>
        </div>

        <div class="field-block">
            <div class="editor-label">
                @Html.LabelFor(model => model.MaxStudents)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.MaxStudents)
                @Html.ValidationMessageFor(model => model.MaxStudents)
            </div>
        </div>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

What I am noticing is that if the user types -1 in the Maximum Seats field, client side validation will fire and let the user know that the value must be between 0 and 32767.

If the user clicks submit, client validation fires again and shows in the validation summary on the top of the form.

If a user enters a valid value in Maximum Seats, but types in a wrong value in the Room Type field, client side validation indicates no errors (which I understand, since Room Type is not being validated on the client), and if the user clicks Submit, the IValidateObject.Validate function is called during the TryUpdateModel() call, which returns a validation error, which then displays on the page in the validation summary on the top of the form.

BUT, if the user types both a wrong number (-1) and an invalid Room Type, and clicks Submit, client side validation will fire BUT the server side validation won't, so they will only see client-side related errors.

Is there a setting, or some trickery in JavaScript I can use to call BOTH the client and server side validation?

If not, I think my only alternative is to do all the validation server side, which will give the user less feedback as they go from field to field, or do the Room Type validation (check the value is in the Room Type table through an ajax call) on the client side, which will duplicate effort.

Any ideas?

Upvotes: 1

Views: 1900

Answers (1)

Jesse van Assen
Jesse van Assen

Reputation: 2290

I think remote validation is what you are looking for. With this, you can do the thing what you mentioned about using ajax for the validation. You do the number validation thing on the client, and the room validation on the server, the results of which get reported with the client side validation.

Upvotes: 2

Related Questions