Hi,
I'm working on a similar problem. My webapp is based on Spring 1.2.8, and currently uses URLs like:
http://myapp.example.com/user/projects/view.html?projectId=1024
I'd like to change the URL scheme so that the resource at the URL above would be available at the URL below:
http://myapp.example.com/user/projects/1024
My solution for extracting identifiers from the URL uses regular expressions with groups:
Code:
Pattern pattern = Pattern.compile("/user/projects/(\\d*)/?$");
Matcher matcher = pattern.matcher(request.getServletPath() + request.getPathInfo());
long projectId = Long.parseLong(m.group(1));
By changing the regex pattern, you can easily extract as many embedded parameters as you need.
On the other side of things, when a controller's response needs to redirect the browser back to a URL with identifiers (e.g. after successfully editing a project, redirect back to view that project), it uses org.springframework.web.servlet.view.RedirectView for generation of URLs, passing in a model with name/value pairs (e.g. model.put("projectId", id)). The problem is that these model values are appended as HTTP querystring paramters. For these cases, I created a new org.springframework.web.servlet.View implementation (extends org.springframework.web.servlet.view.AbstractUrlBa sedView) that behaves just like RedirectView, but uses java.text.MessageFormat to insert values into a template URL:
Code:
protected void renderMergedOutputModel(Map model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
// prepare target URL
StringBuffer targetUrl = new StringBuffer();
if (getUrl().startsWith("/")) {
targetUrl.append(request.getContextPath());
}
MessageFormat format = new MessageFormat("/user/projects/{0}");
targetUrl.append(format.format(model.values().toArray()));
response.sendRedirect(response.encodeRedirectURL(targetUrl.toString()));
}
This all works, but there are several concerns:
- Using regular expressions might be overkill for such simple URL parsing.
- I've put the regular expression logic into a helper class; the controller constructs a member instance with the regex pattern, and then calls a method for each parameter it needs to extract from the URL. To keep it thread-safe, the Matcher must be instantiated again for each method call. In my particular case, it's unlikely that my controllers will need to extract more than two or three parameters from the URL, but is there reason to be concerned about object creation and the ensuing gargabe collection? Is Matcher a heavyweight object?
- I might also want to extract parameters from the URL in an interceptor, but this requires additional regular expression objects. This results in more object creation for the additional Pattern and Matcher instances.
- My RedirectView implementation converts the incoming model map to an object array in order to insert the values into the target URL. If the model is an unsorted Map, then the ordering of those values is undetermined. If you have only one value, it doesn't matter. But with multiple values, you have to use a sorted map; otherwise the values may be inserted into the wrong places. This smacks of bad design.
Another approach for extraction would be an interceptor that simply parses the URL path components into string array or collection (could be as simple as java.lang.String.split(regex)), and put that collection into the request as an attribute. Controllers would presumably know which component represented which identifier, and could then retreive the correct component (converting to the appropriate datatype as necessary). I don't have any other ideas for the redirection issue, though.
I checked Spring 2.0 for better solutions, but didn't see anything. Maybe I missed something. Any better ideas out there?