Profile picture Schedule a Meeting
c a n d l a n d . n e t

What’s Needed for Castle Validation To Work

Dusty Candland | |

Make sure the view knows what to validate

The object or object type to be validated must be sent to the view.

PropertyBag["signuptype"] = typeof(SignupInfo);
// or
PropertyBag["signup"] = new SignupInfo;

Remember to use Flash if redirecting, like in the case of a validation error on the server side.

Make sure the JS files are included

Either just include the files with a script tags:

<script type="text/javascript" src="${siteroot}/Content/js/prototype.js"></script>
<script type="text/javascript" src="${siteroot}/Content/js/scriptaculous.js"></script>
<script type="text/javascript" src="${siteroot}/Content/js/formhelper.js"></script>
<script type="text/javascript" src="${siteroot}/Content/js/behaviour.js"></script>

Or using the InstallScripts method of the respective helpers. With the brail view engine it would be:

${Ajax.InstallScripts()}        
${Scriptaculous.InstallScripts()}        
${FormHelper.InstallScripts()}

Make sure to use the FormHelper

Make sure you use the FormHelper for at least the start and end form tags, but it should be used for the fields too if you want the values auto populated on an exception.

${Form.FormTag({@area:'', @controller:'WaitingList', @action:'AddToList', @immediate:'true', @useLabels:'true'})}
<?brail OutputSubView('/shared/errorsummary') ?>
<div class="formrow">
${Form.LabelFor("signup.Name", "Name:")}
${Form.TextField("signup.Name", {@style: 'width: 300px;'})} <br/>
</div>
<div class="formrow">
${Form.LabelFor("signup.Email", "Email Address:")}
${Form.TextField("signup.Email", {@style: 'width: 300px;'})} <br/>
</div>
<div class="formrow">
${Form.Submit("Add Me")}
</div>
${Form.EndFormTag()}

The immediate and useLabels are optional arguments for the validation JS.

Make sure your controller class inherits  SmartDispatchController

This will already be the case most of the time, but SmartDispatchController knows to validate the bound objects.

public class WaitingListController : SmartDispatcherController

Make sure you have validation attributes on your Model

There are a lot of build in validation attributes and a way to add custom attributes. There is also a way to bind the validation to a model without attributes, but I'm not going to cover that here.

public class EmailInfo
{
    [ValidateNonEmpty]
    public string Name { get; set; }

    [ValidateNonEmpty, ValidateEmail]
    public string Email { get; set; }
}

The Validation Attributes are in the Castle.Components.Validator namespace.

Make sure you check validation on your Action

You will most likely already have a DataBind attribute on your model parameter, but you will need to add Validate = true. Within you action, check if you have validation errors and redirect to the form if you do. Be sure to Flash the model instance and the error summary. This is important as the client might have disabled javascript and because some validators are server side only.

public void AddToList([DataBind("signup", Validate=true)] EmailInfo emailInfo)
{
    if (HasValidationError(emailInfo))
    {
        Flash["signup"] = emailInfo;
        Flash["summary"] = GetErrorSummary(emailInfo);
        RedirectToAction("index");
        return;
    }

    EmailAddress address = new EmailAddress(emailInfo.Name, emailInfo.Email);
    _waitingListRepository.Put(address);

    WaitingListAdminController.WAITING_LIST_KEY);
    Redirect("waitinglist", "onlist");
}

Unit Testing

Unit testing is obviously not required for this to work. If you are testing and using the TestSupport classes with BaseControllerTest you will need to add the following before calling your action to test an invalid model instance. There may be a better way to test this, but here's what I did.

[Test]
public void Add_To_List_Expect_Validate_Fail_Redirect()
{
    EmailInfo info = new EmailInfo();

    if (!_controller.Validator.IsValid(info))
        _controller.PopulateValidatorErrorSummary(info, _controller.Validator.GetErrorSummary(info));
                
    _controller.AddToList(info);
        
    Assert.That(Response.WasRedirected);
    Assert.That(Response.RedirectedTo, Is.EqualTo("/Controller/index.castle"));
}

There's a lot covered here, but really it's not much more code then what you'd have without validation and you get a robust validation framework with both client and server side validation.

Webmentions

These are webmentions via the IndieWeb and webmention.io. Mention this post from your site: