Click here to Skip to main content
11,410,333 members (61,075 online)
Click here to Skip to main content

Validating incoming JSON using Upida

, 7 Jan 2015 CPOL
Rate this:
Please Sign up or sign in to vote.
Web development using JSON is simple

Introduction

In the previous article I described how Upida helps solving common problems in implementing Spring Mvc-based web applications. This article shows how Upida can also greatly simplify your validation routines.

*Note, this article assumes that you have gone through my previous article.

Background

If you apply the techniques from my previous article in your web application, it can greatly reduce the amount of custom coding. But there is still one very important and useful feature in Upida - validation.

The implementation is pretty simple. First of all, you have to identify classes that require validation, usually these are domain classes. Secondly, you have to identify validation methods for each class - for ex. class Client has two methods - validation before save and validation before update. This means that the same class - Client can be validated in two different ways - for save and for update. Sometimes you may require different validation methods - for ex. Assign or Merge or whatever. And the last step is the actual implementing of these validation methods. For ex. the Client class must have two validation methods - validateForSave() and validateForUpdate().

Implementing

Lets create these validation methods for the Client class. In order to follow all the SOLID principles I will create a separate class - ClientValidator which will contain these validation methods. The main idea of the Upida-based validation is the following - You must create a new instance of the UpidaValidationContext class everytime you need to validate something. Everytime you find an error, you must register it in the context instance using its methods. Using the context instance insures that each error message is tied with the corresponding property path. By the way, the context class already contains several simple validation routines, for ex. it can check whether a particular field is null, or if it is present in JSON, it can check text length or collection's size, it can check regular expressions etc. As you must know Upida manages data deserialization, and it stores information about each JSON field, therefore later you can validate if a field was present in JSON, if it was null or if it was correctly parsed during deserialization. This information is accessible through the UpidaValidationContext class methods.

One the main goals of the UpidaValidationContext class is to keep track of the property path. For ex. when you validate an object and then validate its children, the context class ensures that all error messages are connected with corresponding property paths. The result of validation is a list of failures, where a failure is a property path text and a message. This failure structure is serializaed to JSON and sent back to browser, where it is parsed and displayed correctly in correct places in HTML.

The best practice would be deriving from the UpidaValidationContext class, extending some additional application-specific validation routines there and then using the subclass in the validation methods. Below is the example of how to extend the context class:

public class ValidationContext extends UpidaValidationContext implements IValidationContext {

	public ValidationContext(IUpidaContext context) {
		super(context);
	}

	@Override
	public void required() {

		this.mustBeAssigned("is required");
		if (this.isFieldValid() && this.isValidFormat())
		{
			this.mustBeNotNull("is required");
		}
	}

	@Override
	public void missing() {

		this.mustBeNotAssigned("must be empty");
	}

	@Override
	public void number() {

		this.mustBeValidFormat("must be valid number");
	}

	@Override
	public void date() {

		this.mustBeValidFormat("must be valid date");
	}

	@Override
	public void floating() {

		this.mustBeValidFormat("must be valid floating point number");
	}

	@Override
	public void email() {

		this.mustRegexpr("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b", "must be valid Email");
	}

	@Override
	public void text() {

		this.mustHaveLengthBetween(3, 20, "must be between 3 and 20 characters");
	}

	@Override
	public void trueFalse() {

		this.mustBeValidFormat("must be 'yes' or 'no'");
	}
} 

The logic above is extremely helpfull and will make the validation methods extremely simple and readable. For ex. I will no longer have to write redundant error messages for strings between 3 and 20 characters, I will use text(). The same is for dates, doubles, numbers, required fields etc.

In the following piece of code you can see how validation method for Save is implemented validateForSave():

@Service
public class ClientValidator implements IClientValidator {

	@Autowired
	public ILoginValidator loginValidator;

	@Override
	public void validateForSave(Client target, IValidationContext context) {

		context.setField("id", target.getId());
		context.missing();
		
		context.setField("name", target.getName());
		context.required();
		context.text();
		
		context.setField("lastname", target.getLastname());
		context.required();
		context.text();
		
		context.setField("age", target.getAge());
		context.required();
		context.number();
		context.mustBeGreaterThan(0, "must be greater than zero");
		
		context.setField("logins", target.getLogins());
		context.required();
		context.mustHaveCountBetween(1, 5, "must be at least one login");
		
		context.addNested();
		int index = 0;
		for (Login login : target.getLogins())
		{
			context.setIndex(index++);
			context.setTarget(login);
			this.loginValidator.validateForSave(login, context);
		}

		context.removeNested();
	}
}

The setField() method tells the context what field is currently validated, i.e. if you register a failure in the context it will have property path of the current field. Validation routines must go after the setFiled() method call. The UpidaValidationContext class contains numerous validation routines. These routines usually do two actions: first - they check a condition (for ex. check if field value is null), and second - they register a failure if the condition is not true (using the fail() method). For ex. mustBeAssigned() - checks if field is assigned (is present in JSON) and if false - it registers a failure using the current property path; mustBeNotNull(), mustBeNull() are selfdescriptive; mustBeValidFormat() - registers a failure if a field value is not correctly parsed (integer or double).

As you have noticed the UpidaValidationContext class, besides the validation routines methods, also contains other important methods: setTarget() sets up a current validated object, this method is important and should always be called before any validation routine; addNested() - this method propogates current property in the property path as nested object, all subsequent calls to setField() will result in concatenating property name with the nested object name; the removeNested() method does the inverse; the setIndex() method also adds indexing to the current property path - "[" + index + "]".

Here you can see validation methods for the Login class.
 

@Service
public class LoginValidator implements ILoginValidator {

	@Override
	public void validateForSave(Login target, IValidationContext context) {

		context.setField("id", target.getId());
		context.missing();
		
		context.setField("name", target.getName());
		context.required();
		context.text();
		
		context.setField("password", target.getPassword());
		context.required();
		context.text();
		
		context.setField("enabled", target.getEnabled());
		context.trueFalse();
		
		context.setField("client", target.getClient());
		context.missing();
	}
} 

When the validators for all the domain and DTO classes are done, we are free to define a facade class which will be injected into our services or controllers and which will be used to fire validation. Here is an example of the validation facade:

@Service
public class ValidationFacade implements IValidationFacade {

	@Autowired
	public IValidationContextFactory contextFactory;

	@Autowired
	public IClientValidator clientValidator;

	// @Autowired
	// other validators

	@Override
	public void assertClientForSave(Client target) {

		IValidationContext context = this.contextFactory.getNew();
		context.setTarget(target);
		this.clientValidator.validateForSave(target, context);
		context.assertValid();
	}

	@Override
	public void assertClientForUpdate(Client target) {

		IValidationContext context = this.contextFactory.getNew();
		context.setTarget(target);
		this.clientValidator.validateForUpdate(target, context);
		context.assertValid();
	}
}

The class looks pretty simple, here I use simple factory method to get a new instance of the validation context. The most important is the last line of each method - assertValid() - this method throws ValidationException if at least one error is registerd in the context, otherwise it does nothing.

Here yuo can see how the facade is injected and used in the service layer:

@Service
public class ClientService implements IClientService {

	@Autowired
	public IMapper mapper;

	@Autowired
	public IValidationFacade validator;

	@Autowired
	public IClientDao clientDao;


	@Override
	public void save(Client item) {
		this.validator.assertClientForSave(item);
		this.mapper.map(item, Client.class);
		this.clientDao.save(item);
	}

	@Override
	public void update(Client item) {
		this.validator.assertClientForUpdate(item);
		Client existing = this.clientDao.load(item.getId());
		this.mapper.mapTo(item, existing, Client.class);
		this.clientDao.merge(existing);
	}
}

By the way, the validation context class is quite handy and easy to extend and use it differently. You can allways play with its methods and get a different behavior. In the example code you will see how it is used to for validation in different circumstances - for ex. validation before Delete. Please, see the assertClientExists() and the assertMoreThanOneClient() methods in the ValidationFacade class.

	@Override
	public void assertClientExists(Client item) {

		if (item == null)
		{
			IValidationContext context = this.contextFactory.getNew();
			context.fail("Client does not exist");
			context.assertValid();
		}
	}

	@Override
	public void assertMoreThanOneClient(long count) {

		if (count == 1)
		{
			IValidationContext context = this.contextFactory.getNew();
			context.fail("Cannot delete the only client");
			context.assertValid();
		}
	}

In these methods you don't rely on a target validated object. You just have to assert that some condition is met. In this case a ValidationException is thrown with jsut on failure in it, and property path is empty.

And that is it. Now validation must work. If validation succeeds, nothing happens. If validation fails, ValidationException is thrown. ValidationException contains a list of name-value pairs - property path and error message. In order to process this exception properly in Spring Mvc, I will create a method in my controller, and mark it with @ExceptionHandler annotation. This technique is common in Spring Mvc, while working with exceptions. Here is implementation of this method.

@ExceptionHandler
@ResponseBody
public FailResponse handleError(Exception ex, HttpServletResponse response) {

    FailResponse fail = null;
    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    if(ex instanceof ValidationException) {
        fail = ((ValidationException) ex).buildFailResponse();
    }
    else {
        fail = new FailResponse(ex.getMessage());
    }

    return fail;
}

I replace the HTTP response content with my JSON array of (property path - error message) pairs - FailResponse class.

Now, every time the ValidationException is thrown, it will be handled by the handleError() method, and a list of property paths and error messages will be sent back as JSON in the HTTP response.

Front-end

The last step is displaying those messages in the correct place in HTML. You are free to write your custom JavaScript for AngularJS or KnockoutJS, or anything else. Upida comes with a small JavaScript library for AngularJS, which makes it simpler displaying those errors. For ex. if you use angular, your HTML must look like this:

<label>Name:</label>
<input type="text" ng-model="name" />
<span class="error" up-error-key="name" up-error-body></span> 

The up-error-key directive indicates property path, the up-error-body indicates that error text will replace the body of the span element. Sometimes up-error-key can be more sophisticated - see "Create Client" form.

You can find out how to create a Single-Page Application (SPA) using AngularJS in my next article: AngularJS single-page app and Upida.Net.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

vladimir husnullin
Software Developer
United States United States
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150414.5 | Last Updated 7 Jan 2015
Article Copyright 2014 by vladimir husnullin
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid