views:

118

answers:

4

Where do you do validation in a webapp (backend)?

Option #1: Service layer?

UserService.validate(FORM);  // verify and returns struct of errors

Option #2: Object layer, on setter? e.g.

user.setEmail(email);    // throws invalid/used e-mail

Option #3: Object layer, validate()? e.g.

user.init(FORM);    // accept any values, no type checking
user.validate();    // returns struct of errors

What's your take? Thanks!

+4  A: 

You do validation at each phase, but for slightly different reasons.

You validate when the user sets a value in order to provide immediate feedback to the user about whether the input is valid. That validation should only be used to enhance the user experience. You can validate while the user is typing if that's appropriate, but be sure not to prevent the user from entering invalid partial input, since there might be more coming and you don't want the validation to get in the way.

You validate before the user submits the form in order to ensure that the submission will be valid without incurring the time cost of a full round-trip to the server. This would mainly be for things that weren't or couldn't be validated during user entry, and it might involve some communication with the server, to check whether a username is available for example, without reloading the page. This stage is also mainly for the user's benefit. Whether you validate each item during entry or on submit is up to you and should depend on which provides a better user experience and better fits the user's mental model.

Finally, you need to validate everything when it comes back to the server because you can't trust the browser. This validation is mainly for security. You can never assume that any data coming from your client is clean because it might not be coming from your client. It could be coming from a hostile agent that's emulating your client. So validate everything, completely, for all potential exploits and other invalid conditions, regardless of whether it was validated in the client.

Hope that helps.

jbourque
Sorry, my question is actually, in the Objects layer (backend), where do you validate?
Henry
A: 

First of all, +1 from me for jborque.

I would like to add that input-type validations are very repetitive, e.g.

UI: Don't allow Name to be longer than 30 characters BIZ: Throw an exception / create broken rule if name is longer than 30 characters DB: Make Name column 30 characters wide UNIT TEST: Test names < 30, > 30, exactly 30 characters long

This is a GREAT candidate for code generation. If 30 suddenly changes to 40 and you are using code generation, making the change is as easy as re-running the code generator (and creating DB upgrade scripts for any production data).

I have done this in the past using a UML modeling tool to capture input rules and partial classes in C# to separate code generated from the UML model from my own hand-written code. The same concept can be applied easily in a number of development environments.

Eric J.
A: 

+1 for jbourqu also. Nice answers. Everywhere you can without incurring unreasonable performance cost. I quite often validate the inputs to functions in the backend that arn't directly exposed in case some other code calls them with unexpected values or mixes the parameter order up.

A: 

I typically keep my form validation in the service layer, rather than with the objects themselves. I do this not because I don't agree with keeping the objects being wholly encapsulated, I do it because it fits the methodology/design pattern that I choose use in my sites for handling form submissions.

I think frameworks like Transfer encourage you to keep object validation within the objects where as engines/frameworks like Alegad's Validat are designed to keep validation outside of it. I don't think either approach is wrong, its really just a matter of what you prefer (and what fits the application).

Out of the 3 options though, option #2 is miss-use of exception handling for situations in which you have expected outcomes. By having a validate method (whether it be on the object or in a service) you can control the erroneous validation situations more gracefully than catching exceptions and bubbling them up passively (capture and return a struct with info on the failure) or explicitly (rethrow, ect..).

jalpino
I don't think option #2 is a misuse of exception. When you call setX(y), it should expect y to be of a type X expects, and validate if y is a valid value for whatever X is. Anyone agree?
Henry