Broken Exceptions
What caught up with me using that approach was using Spring exception handling. Adding an exception handler to a controller, for instance, can prevent you from having to worry about system errors in addition to expected error conditions. You obviously shouldn't be doing much with Spring form binding in your exception handling, but it's nice to be able to do something like throw the user back out to the form they were using (or another form for that matter) with an appropriate message. For instance I was working on a form that was processed locally before communicating with a remote service. In the case of most errors I wanted to give the user a chance to try again, but I didn't want to pollute the local code with _all_ of the remote concerns.
Into the View Resolver
But where? Like before I wanted to keep this piece decently modularized and Spring didn't provide much to help. After fishing through the source code for a bit the best place seemed to be to intercept the call to render() on the View interface.public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
This can be modified using a Decorative wrapper:
public class RequestContextViewDecorator implements View { private final View innerView; private final ApplicationContext applicationContext; public RequestContextViewDecorator(View innerView, ApplicationContext applicationContext) { this.innerView = innerView; this.applicationContext = applicationContext; }
The first issue is getting around that pesky wildcard capture "?" in the model. This is easily done by a small method to get back a known safe type:
private Map<String, Object> getTypedMap(Map<String, ?> model) { Map%lt;String, Object> typedMap = new HashMap%lt;String, Object>(); typedMap.putAll(model); return typedMap; }
This could easily be refactored in to an abstract class and then use a Template abstract method which receives the typed map if you have other classes doing similar things or like to add extra classes to keep things focused. This object then inherits the same behavior covered from the first post, throwing what it needs in the model and then delegating to the wrapped View:
@Override public void render(Map%lt;String, ?> untypedModel, HttpServletRequest request, HttpServletResponse response) throws Exception { Mapmodel = getTypedMap(untypedModel); if (exposeSpringMacroHelpers) { if (!model.containsKey(MODEL_KEY)) { model.put(MODEL_KEY, new RequestContext(request, response, ((WebApplicationContext) applicationContext).getServletContext(), model)); } } .... innerView.render(model, request, response); }
Now just plug in to to your view resolver (in this case the Surf(again) view resolver):
public class RequestContextPageViewResolver extends PageViewResolver { @Override protected View loadView(String viewName, Locale locale) throws Exception { return new RequestContextViewDecorator(super.loadView(viewName, locale), this.getApplicationContext()); } }
And there you have it (after you wire in your View resolver of choice in your Spring config): a nice OOP way of integrating the request context needed for Spring form binding in a modular way to a View resolver which for some reason or another does not have the functionality in its inheritance hierarchy.
No comments :
Post a Comment