Stripes

Friendly URL support

Details

  • Type: New Feature New Feature
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: Release 1.5
  • Component/s: ActionBean Dispatching
  • Labels:
    None

Description

One thing that comes up frequently is support for friendly URLs, e.g.:
/blog/2006/08/22
/user/6282/edit

and so on. While it's possible to acheive URLs like this using 3rd party tools like UrlRewriteFilter it would be nice if they were built directly into stripes because then all URL information could be kept in a single place for each class.

I'm envisaging an annotation something like this:
@UrlInfo("/{year}/{month}/{day}")
@UrlInfo("/{userId}/{event}")
that would inform Stripes how to map the extra pieces of information encoded in the URL.

Optionally this could also be specified with the existing UrlBinding annotations, e.g.
@UrlBinding("/blog/{year}/{month}/{day}")

If done right, the stripes url and link tags could also take advantage of this information to put certain parameters into the URL instead of a parameter string.

I'm very open to hearing alternative ideas around how to specify this, and other functionality that would be desirable.

Activity

Hide
Tim Fennell added a comment - 04/Sep/06 4:26 PM

From Barry Davies on the stripes-dev list:

Would any of the following also be what you had in mind:

  • the URL-parameter values will go thru the registered converters when they are injected into the ActionBean
  • this would probably happen before any form submission values are injected.
  • Stripes URL creation and linking tags would use the registered formatter (and then URL encode it) to create the URL
  • Those URL and link tags would know how to create the URL (param order) based on the annotation in the ActionBean
Show
Tim Fennell added a comment - 04/Sep/06 4:26 PM From Barry Davies on the stripes-dev list: Would any of the following also be what you had in mind:
  • the URL-parameter values will go thru the registered converters when they are injected into the ActionBean
  • this would probably happen before any form submission values are injected.
  • Stripes URL creation and linking tags would use the registered formatter (and then URL encode it) to create the URL
  • Those URL and link tags would know how to create the URL (param order) based on the annotation in the ActionBean
Hide
Kai Grabfelder added a comment - 27/Sep/06 3:46 AM

I would really like to see this in stripes as well .

Would using the UrlBinding annotation really work? I thought that it is only appropriate on class level and not on method level?

Anyway I vote for this issue

Show
Kai Grabfelder added a comment - 27/Sep/06 3:46 AM I would really like to see this in stripes as well . Would using the UrlBinding annotation really work? I thought that it is only appropriate on class level and not on method level? Anyway I vote for this issue
Hide
Kai Grabfelder added a comment - 27/Sep/06 3:48 AM

see also STS-268

Show
Kai Grabfelder added a comment - 27/Sep/06 3:48 AM see also STS-268
Hide
Paul Barry added a comment - 25/Oct/06 11:35 AM

I think it would be great to add this. As
James Strachan suggested in STS-268, it would be great if there is a
LifecycleStage for UrlParameter binding. This would make CRUD-type
action beans easier to code, because you could just do this:

@UrlBinding(/user/{id})
public class UserActionBean implements ActionBean {

private Long id;
private User user;

@DefaultHandler
public Resolution get() { return new ForwardResolution("/WEB-INF/views/user.jsp"); }

public Resolution save() {

}

@Before(stage=LifecycleStage.UrlParameterBinding)
public void loadUser() {
if(id != null) { user = userService.get(id); }
}

}

Or the safer, more verbose way:

@UrlBinding(/user/{event}/{id})
public class UserActionBean implements ActionBean {

private Long id;
private User user;

public Resolution add() { return new ForwardResolution("/WEB-INF/views/user.jsp"); }

public Resolution edit() { return new ForwardResolution("/WEB-INF/views/user.jsp); }

public Resolution create() { userService.create(user); return new RedirectResolution(SomeOtherActionBean.class); }

public Resolution update() { userService.update(user); return new RedirectResolution(SomeOtherActionBean.class); }

@Before(stage=LifecycleStage.UrlParameterBinding, on={"edit","update"})
public void loadUser() { //Will throw exception if id is null or user is not found user = userService.get(id); }

}

To support this, will there be some kind of stripes url tag that will
allow you to generate a url to this event? Maybe like this:

<stripes:url class="com.mycompany.stripes.UserActionBean">
<stripes:urlParam name="id" value="${user.id}"/>
<stripes:urlParam name="event" value="edit"/>
</stripes:url>

Show
Paul Barry added a comment - 25/Oct/06 11:35 AM I think it would be great to add this. As James Strachan suggested in STS-268, it would be great if there is a LifecycleStage for UrlParameter binding. This would make CRUD-type action beans easier to code, because you could just do this: @UrlBinding(/user/{id}) public class UserActionBean implements ActionBean { private Long id; private User user; @DefaultHandler public Resolution get() { return new ForwardResolution("/WEB-INF/views/user.jsp"); } public Resolution save() { } @Before(stage=LifecycleStage.UrlParameterBinding) public void loadUser() { if(id != null) { user = userService.get(id); } } } Or the safer, more verbose way: @UrlBinding(/user/{event}/{id}) public class UserActionBean implements ActionBean { private Long id; private User user; public Resolution add() { return new ForwardResolution("/WEB-INF/views/user.jsp"); } public Resolution edit() { return new ForwardResolution("/WEB-INF/views/user.jsp); } public Resolution create() { userService.create(user); return new RedirectResolution(SomeOtherActionBean.class); } public Resolution update() { userService.update(user); return new RedirectResolution(SomeOtherActionBean.class); } @Before(stage=LifecycleStage.UrlParameterBinding, on={"edit","update"}) public void loadUser() { //Will throw exception if id is null or user is not found user = userService.get(id); } } To support this, will there be some kind of stripes url tag that will allow you to generate a url to this event? Maybe like this: <stripes:url class="com.mycompany.stripes.UserActionBean"> <stripes:urlParam name="id" value="${user.id}"/> <stripes:urlParam name="event" value="edit"/> </stripes:url>
Hide
Walter Rumsby added a comment - 28/Nov/06 11:35 AM

It's not just about the URL pattern, the HTTP method should also come into play - a DELETE to a particular URL is quite different to a GET (although the DispatcherServlet seems to route GETs via the doPost method).

So, IMHO, the binding should allow specification of both a UrlBinding and a HttpMethod

Show
Walter Rumsby added a comment - 28/Nov/06 11:35 AM It's not just about the URL pattern, the HTTP method should also come into play - a DELETE to a particular URL is quite different to a GET (although the DispatcherServlet seems to route GETs via the doPost method). So, IMHO, the binding should allow specification of both a UrlBinding and a HttpMethod
Hide
Remi VANKEISBELCK added a comment - 11/Jan/07 11:09 AM

Here under is a patch that shows a possible implementation, using "clean url binding expressions" like this :

It's been discussed in the stripes ML : search for : "Clean URLs : a simple proposal".

See attached patch

Show
Remi VANKEISBELCK added a comment - 11/Jan/07 11:09 AM Here under is a patch that shows a possible implementation, using "clean url binding expressions" like this : It's been discussed in the stripes ML : search for : "Clean URLs : a simple proposal". See attached patch
Hide
Remi VANKEISBELCK added a comment - 11/Jan/07 11:09 AM

the patches

Show
Remi VANKEISBELCK added a comment - 11/Jan/07 11:09 AM the patches
Hide
Remi VANKEISBELCK added a comment - 15/Jan/07 10:41 AM

Been reworking that a little...

It was actually possible to decouple everything from Stripes : this new version allows to use clean URLs as a "plugin" (or "extension"), using a custom ActionResolver, Interceptor and request wrapper. I didn't realize this at first sight.

The principle is still the same (I haven't changed the syntax, it's no big deal) : you define the clean URL expressions in the regular @UrlBinding annot, using '@' and ':' to specify the action's name as well as the request parameters, and your actions can handle clean URLs, with the binding etc.

I've deployed a test page there :
http://195.83.41.200/cleanurls

And you can grab the WAR (including sources, look in WEB-INF/classes) there :
http://jfacets.rvkb.com/pub/cleanurls.war

All this is VERY simple (4 classes !!!). The process goes like this :
1/ Handle the request (e.g. http://.../myApp/actions/store/Book/1)
2/ CleanUrlActionResolver is used to locate the action bean
3/ The bean is obtained
4/ The CleanUrlInterceptor detects clean URL handling and thereby wraps the request (once more) and reassociates this new top-level wrapper to the current ActionBeanContext (@Before(HandlerResolution))
5/ The top-level wrapper goes through the rest of the lifecycle and makes the clean URL thingy completely transparent : you just your the request the same way, and so does stripes, so binding, validation etc works.

Check it out !

Show
Remi VANKEISBELCK added a comment - 15/Jan/07 10:41 AM Been reworking that a little... It was actually possible to decouple everything from Stripes : this new version allows to use clean URLs as a "plugin" (or "extension"), using a custom ActionResolver, Interceptor and request wrapper. I didn't realize this at first sight. The principle is still the same (I haven't changed the syntax, it's no big deal) : you define the clean URL expressions in the regular @UrlBinding annot, using '@' and ':' to specify the action's name as well as the request parameters, and your actions can handle clean URLs, with the binding etc. I've deployed a test page there : http://195.83.41.200/cleanurls And you can grab the WAR (including sources, look in WEB-INF/classes) there : http://jfacets.rvkb.com/pub/cleanurls.war All this is VERY simple (4 classes !!!). The process goes like this : 1/ Handle the request (e.g. http://.../myApp/actions/store/Book/1) 2/ CleanUrlActionResolver is used to locate the action bean 3/ The bean is obtained 4/ The CleanUrlInterceptor detects clean URL handling and thereby wraps the request (once more) and reassociates this new top-level wrapper to the current ActionBeanContext (@Before(HandlerResolution)) 5/ The top-level wrapper goes through the rest of the lifecycle and makes the clean URL thingy completely transparent : you just your the request the same way, and so does stripes, so binding, validation etc works. Check it out !
Hide
Remi VANKEISBELCK added a comment - 15/Jan/07 10:45 AM

I forgot : it still only works one way : you can't use "beanclass" or link-param inside your links, it appends parameters the "ugly" way (it still works of course, but it's not clean URLs).

So the only way yet to get it done is to construct the "href" yourself, like this :

<stripes:link href="/action/store/${productType}/${productId}">
...

If anyone has an idea about how to do this...

Show
Remi VANKEISBELCK added a comment - 15/Jan/07 10:45 AM I forgot : it still only works one way : you can't use "beanclass" or link-param inside your links, it appends parameters the "ugly" way (it still works of course, but it's not clean URLs). So the only way yet to get it done is to construct the "href" yourself, like this : <stripes:link href="/action/store/${productType}/${productId}"> ... If anyone has an idea about how to do this...
Hide
Remi VANKEISBELCK added a comment - 18/Jan/07 6:52 AM

Hi again folks !

Here is the latest version of the code. It should have no impact on existing stuff, and allows clean URLs without being intrusive with Stripes itself : it's now all about dropping a jar and modifying web.xml... the plugin way

Below is a web.xml filter config example, and I'll attach the zipped sources right now.

Even if you don't plan to implement anything in there, please test that and send some feedback if you can ! It's about 15 minutes to integrate that into your apps and try it out :

1/ include the clean URLs sources to your project or recompile it and include a jar...
2/ modify the Stripes filter config in web.xml as shown below
3/ define a clean url expression for some of your actions, using @UrlBinding (e.g. @UrlBinding("/action/@DoIt/:param/") or @UrlBinding("/store/product/:id/:_eventName) etc.)
4/ start the app, open up your web browser, and issue a GET to your action (e.g. http://.../myApp/action/DoIt/blah or http://.../myApp/store/product/123/show etc.)

I'm using it in some application I develop, and so far it's been working fine : all existing stuff still works, and so does clean URLs for the actions that define clean url expressions in their @UrlBinding...

Thanks in advance for any feedback and help with that.

Cheers !

Remi

------------------------------
web.xml example :

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- Configuration of the Stripes Filter. -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<filter>
<description>
Provides essential configuration and request processing services
for the Stripes framework with clean URLs enabled !
</description>
<display-name>Stripes Filter</display-name>
<filter-name>StripesFilter</filter-name>
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
<init-param>
<param-name>ActionResolver.PackageFilters</param-name>
<param-value>net.sourceforge.jfacets.woko.actions.*</param-value>
</init-param>

<init-param>
<param-name>Configuration.Class</param-name>
<param-value>net.sourceforge.stripes.config.RuntimeConfiguration</param-value>
</init-param>

<!-- clean URLs (action resolver + interceptor)... -->
<init-param>
<param-name>ActionResolver.Class</param-name>
<param-value>net.sourceforge.stripes.cleanurls.CleanUrlActionResolver</param-value>
</init-param>
<init-param>
<param-name>Interceptor.Classes</param-name>
<param-value>
net.sourceforge.stripes.cleanurls.CleanUrlInterceptor,
net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
</param-value>
</init-param>
</filter>

Show
Remi VANKEISBELCK added a comment - 18/Jan/07 6:52 AM Hi again folks ! Here is the latest version of the code. It should have no impact on existing stuff, and allows clean URLs without being intrusive with Stripes itself : it's now all about dropping a jar and modifying web.xml... the plugin way Below is a web.xml filter config example, and I'll attach the zipped sources right now. Even if you don't plan to implement anything in there, please test that and send some feedback if you can ! It's about 15 minutes to integrate that into your apps and try it out : 1/ include the clean URLs sources to your project or recompile it and include a jar... 2/ modify the Stripes filter config in web.xml as shown below 3/ define a clean url expression for some of your actions, using @UrlBinding (e.g. @UrlBinding("/action/@DoIt/:param/") or @UrlBinding("/store/product/:id/:_eventName) etc.) 4/ start the app, open up your web browser, and issue a GET to your action (e.g. http://.../myApp/action/DoIt/blah or http://.../myApp/store/product/123/show etc.) I'm using it in some application I develop, and so far it's been working fine : all existing stuff still works, and so does clean URLs for the actions that define clean url expressions in their @UrlBinding... Thanks in advance for any feedback and help with that. Cheers ! Remi ------------------------------ web.xml example : <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Configuration of the Stripes Filter. --> <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <filter> <description> Provides essential configuration and request processing services for the Stripes framework with clean URLs enabled ! </description> <display-name>Stripes Filter</display-name> <filter-name>StripesFilter</filter-name> <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class> <init-param> <param-name>ActionResolver.PackageFilters</param-name> <param-value>net.sourceforge.jfacets.woko.actions.*</param-value> </init-param> <init-param> <param-name>Configuration.Class</param-name> <param-value>net.sourceforge.stripes.config.RuntimeConfiguration</param-value> </init-param> <!-- clean URLs (action resolver + interceptor)... --> <init-param> <param-name>ActionResolver.Class</param-name> <param-value>net.sourceforge.stripes.cleanurls.CleanUrlActionResolver</param-value> </init-param> <init-param> <param-name>Interceptor.Classes</param-name> <param-value> net.sourceforge.stripes.cleanurls.CleanUrlInterceptor, net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor </param-value> </init-param> </filter>
Hide
Remi VANKEISBELCK added a comment - 18/Jan/07 6:54 AM

includes source code for the clean urls

Show
Remi VANKEISBELCK added a comment - 18/Jan/07 6:54 AM includes source code for the clean urls
Hide
Remi VANKEISBELCK added a comment - 04/Feb/07 8:06 AM

I finally packaged my version as a small extension that can be used before Stripes includes clean URL support :

http://remi.mongus.com/stripes-clean

Check it out !

Show
Remi VANKEISBELCK added a comment - 04/Feb/07 8:06 AM I finally packaged my version as a small extension that can be used before Stripes includes clean URL support : http://remi.mongus.com/stripes-clean Check it out !
Hide
Kai Grabfelder added a comment - 04/Feb/07 8:53 AM

cool thanks

as soon as the link tag with the beanclass attribute works as well it's time for integrating this into the core and a new release g

Show
Kai Grabfelder added a comment - 04/Feb/07 8:53 AM cool thanks as soon as the link tag with the beanclass attribute works as well it's time for integrating this into the core and a new release g
Hide
Ben Gunter added a comment - 12/Jun/07 10:20 PM

An initial version of clean URL support is now available in the trunk. URLs are of the form @UrlBinding("/path/to/bean/{param1}/{param2}"). Parameters are available through the normal means. The s:link and s:url tags and the RedirectResolution and ForwardResolution classes support constructing URLs using the parameters via UrlBuilder.

There are a couple of gotchas at the moment. It seems that parameter values can get duplicated if a request is received with URI parameters and then forwarded with URI parameters. Also, the $event reserved parameter name is not yet supported so things won't work quite right with that sometimes. I'll work on getting that fixed next.

Show
Ben Gunter added a comment - 12/Jun/07 10:20 PM An initial version of clean URL support is now available in the trunk. URLs are of the form @UrlBinding("/path/to/bean/{param1}/{param2}"). Parameters are available through the normal means. The s:link and s:url tags and the RedirectResolution and ForwardResolution classes support constructing URLs using the parameters via UrlBuilder. There are a couple of gotchas at the moment. It seems that parameter values can get duplicated if a request is received with URI parameters and then forwarded with URI parameters. Also, the $event reserved parameter name is not yet supported so things won't work quite right with that sometimes. I'll work on getting that fixed next.
Hide
Remi VANKEISBELCK added a comment - 12/Jun/07 11:38 PM

Yikes ! Thanks Ben !

Why do you say the $event is making things go wrong ? Can we just use {_eventName} ?

Good work

Remi

Show
Remi VANKEISBELCK added a comment - 12/Jun/07 11:38 PM Yikes ! Thanks Ben ! Why do you say the $event is making things go wrong ? Can we just use {_eventName} ? Good work Remi
Hide
Ben Gunter added a comment - 13/Jun/07 6:50 AM

Sorry, I was tired and didn't want to take the time to explain. Here's an example of where it causes a little trouble at the moment. Say you map a bean like so:

@UrlBinding("/action/blog/{_eventName}/{blogEntry}")

And somewhere you redirect to it like so:

return new RedirectResolution(BlogBean.class, "view").addParameter("blogEntry", 12345);

OnwardResolution will just create a parameter called "view" with an empty value, which will result in a redirect to:

/action/blog?view=&blogEntry=12345

By using the special parameter $event, you could map the bean like this:

@UrlBinding("/action/blog/{$event}/{blogEntry}")

And the resulting URL should look like this instead:

/action/blog/view/12345

The difference being that instead of creating an empty parameter named "view", it sets the $event parameter to the value "view".

Unfortunately, using _eventName doesn't exactly solve this problem. Instead of this:

new RedirectResolution(BlogBean.class, "view")

you currently have to do this instead:

new RedirectResolution(BlogBean.class).addParameter("_eventName", "view")

But like I said in my previous comment, I intend to get this fixed soon. I'll have to make UrlBuilder aware of events and how to handle them when constructing the URL. What used to be such a simple class has gotten so much more complex in the last week or two

Show
Ben Gunter added a comment - 13/Jun/07 6:50 AM Sorry, I was tired and didn't want to take the time to explain. Here's an example of where it causes a little trouble at the moment. Say you map a bean like so: @UrlBinding("/action/blog/{_eventName}/{blogEntry}") And somewhere you redirect to it like so: return new RedirectResolution(BlogBean.class, "view").addParameter("blogEntry", 12345); OnwardResolution will just create a parameter called "view" with an empty value, which will result in a redirect to: /action/blog?view=&blogEntry=12345 By using the special parameter $event, you could map the bean like this: @UrlBinding("/action/blog/{$event}/{blogEntry}") And the resulting URL should look like this instead: /action/blog/view/12345 The difference being that instead of creating an empty parameter named "view", it sets the $event parameter to the value "view". Unfortunately, using _eventName doesn't exactly solve this problem. Instead of this: new RedirectResolution(BlogBean.class, "view") you currently have to do this instead: new RedirectResolution(BlogBean.class).addParameter("_eventName", "view") But like I said in my previous comment, I intend to get this fixed soon. I'll have to make UrlBuilder aware of events and how to handle them when constructing the URL. What used to be such a simple class has gotten so much more complex in the last week or two
Hide
Ben Gunter added a comment - 13/Jun/07 7:01 AM

I forgot to mention that parameters can take default values. If the parameter is not present in the URI, then its default value will be appended to the query string on forward. Here's an example.

@UrlBinding("/action/map/{country=US}")

So if the URI is /action/map/CA you'll get a map of Canada, but if it's just /action/map then you'll get a map of the default country, in this case US.

Escaping is also supported with backslashes in case you want to embed something weird:

@UrlBinding("/action/this
{is\\}weird")

Show
Ben Gunter added a comment - 13/Jun/07 7:01 AM I forgot to mention that parameters can take default values. If the parameter is not present in the URI, then its default value will be appended to the query string on forward. Here's an example. @UrlBinding("/action/map/{country=US}") So if the URI is /action/map/CA you'll get a map of Canada, but if it's just /action/map then you'll get a map of the default country, in this case US. Escaping is also supported with backslashes in case you want to embed something weird: @UrlBinding("/action/this
{is\\}weird")
Hide
Aaron White added a comment - 01/Oct/07 12:03 PM

This does not seem 'done' to me, so I'm not sure why it's marked as resolved. I feel this is a very important feature for stripes, and so I hope this helps:

1) What changes need to be made to my web.xml to support this? Once I add a URL Binding of "/resource/{whatever}" it no longer get's dispatched to my ActionBean because it doesn't end in .action, etc. Seems some info should be provided lest I have to unecessarily nest custom urls under "/virtual/..."

2) The stripes:link tag should ABSOLUTELY use the URLBinding information to generate the href. This is very very important, and would put stripes in the unique place of abstracting URL management to the most comfortable place.

Show
Aaron White added a comment - 01/Oct/07 12:03 PM This does not seem 'done' to me, so I'm not sure why it's marked as resolved. I feel this is a very important feature for stripes, and so I hope this helps: 1) What changes need to be made to my web.xml to support this? Once I add a URL Binding of "/resource/{whatever}" it no longer get's dispatched to my ActionBean because it doesn't end in .action, etc. Seems some info should be provided lest I have to unecessarily nest custom urls under "/virtual/..." 2) The stripes:link tag should ABSOLUTELY use the URLBinding information to generate the href. This is very very important, and would put stripes in the unique place of abstracting URL management to the most comfortable place.
Hide
Ben Gunter added a comment - 01/Oct/07 12:09 PM

1) The one change that is required in web.xml is <dispatcher>FORWARD</dispatcher> must be added to the StripesFilter mappings. Other than that, you just have to make sure your URLs always begin with a prefix or end with a suffix that maps to the Stripes dispatcher servlet.

2) It already does this.

Show
Ben Gunter added a comment - 01/Oct/07 12:09 PM 1) The one change that is required in web.xml is <dispatcher>FORWARD</dispatcher> must be added to the StripesFilter mappings. Other than that, you just have to make sure your URLs always begin with a prefix or end with a suffix that maps to the Stripes dispatcher servlet. 2) It already does this.
Hide
Aaron White added a comment - 23/Oct/07 6:50 PM

Thanks for the response Ben

1) I did setup the forward, and that's why I mentioned having to unecessarily nest urls under "/virtual". I'm trying to get rails-esque URLs, for a webapp rooted at "/" and don't want a extraneous (from a URL engineering point of view) directory or suffix

2) I couldn't make it work, I will try again

Show
Aaron White added a comment - 23/Oct/07 6:50 PM Thanks for the response Ben 1) I did setup the forward, and that's why I mentioned having to unecessarily nest urls under "/virtual". I'm trying to get rails-esque URLs, for a webapp rooted at "/" and don't want a extraneous (from a URL engineering point of view) directory or suffix 2) I couldn't make it work, I will try again
Hide
Andrew Jaquith added a comment - 11/Jul/08 12:17 AM

Ben,

I've found an issue in build 947 with the implementation of clean URLs, which relates to differences in how the ActionResolver and UrlBuilder handle appended parameters.

With a given "clean" @UrlPattern, if parameters otherwise unspecified in the pattern are added to the UrlBuilder, they are properly appended in the generated URL. But if that generated URL is passed back to the StripesFilter, the trailing parameters are not parsed as expected, even if the ActionBean has properties that those parameters "should" bind do. In other words, it is not a "round trip" operation.

Example: suppose we have an ActionBean with @UrlBinding("/cleanbean1/{param1=Test}/?do={$event}"). It has three properties, param1, param2 and param3 and two events (view, edit). Suppose we create an UrlBuilder for the bean; specify an event of "edit"; and add parameter values for the three properties (param1=Godel, param2= Escher, param3= Bach). We get a properly built URL, with param2 and param3 appended after the clean URL portion, e.g.:

"/cleanbean2/Godel/?do=edit&param2=Escher&param3=Bach"

However, passing that URL back into the StripesFilter via MockRoundTrip results in the trailing parameters not being parsed:

param1=Godel, param2=null, param3=null

I've looked into this a little bit further, and it seems that the MergedParameterMap feels that "edit&param2=Escher&param3" is the extracted parameter and that its value should be "Bach". That's probably not the root of the issue, but it does suggest that trailing parameters for clean URLs aren't being parsed.

This might be "by design," – but it feels kinda asymmetrical. Can this be fixed?

I have a TestNG class that tests several edge cases, if that's helpful.

Show
Andrew Jaquith added a comment - 11/Jul/08 12:17 AM Ben, I've found an issue in build 947 with the implementation of clean URLs, which relates to differences in how the ActionResolver and UrlBuilder handle appended parameters. With a given "clean" @UrlPattern, if parameters otherwise unspecified in the pattern are added to the UrlBuilder, they are properly appended in the generated URL. But if that generated URL is passed back to the StripesFilter, the trailing parameters are not parsed as expected, even if the ActionBean has properties that those parameters "should" bind do. In other words, it is not a "round trip" operation. Example: suppose we have an ActionBean with @UrlBinding("/cleanbean1/{param1=Test}/?do={$event}"). It has three properties, param1, param2 and param3 and two events (view, edit). Suppose we create an UrlBuilder for the bean; specify an event of "edit"; and add parameter values for the three properties (param1=Godel, param2= Escher, param3= Bach). We get a properly built URL, with param2 and param3 appended after the clean URL portion, e.g.: "/cleanbean2/Godel/?do=edit&param2=Escher&param3=Bach" However, passing that URL back into the StripesFilter via MockRoundTrip results in the trailing parameters not being parsed: param1=Godel, param2=null, param3=null I've looked into this a little bit further, and it seems that the MergedParameterMap feels that "edit&param2=Escher&param3" is the extracted parameter and that its value should be "Bach". That's probably not the root of the issue, but it does suggest that trailing parameters for clean URLs aren't being parsed. This might be "by design," – but it feels kinda asymmetrical. Can this be fixed? I have a TestNG class that tests several edge cases, if that's helpful.
Hide
Andrew Jaquith added a comment - 11/Jul/08 8:26 AM

I've attached a patch for this that fixes the issue. It adds a separator() method to the UrlBinding annotation (which defaults to "&"), and an equivalent getSeparator() method to the UrlBinding class. It also adds some processing logic to UrlBindingFactory to properly recognize when a clean URL that matches a prototype might have appended parameters.

The patch includes a CleanUrlBindingTests file that exercises several edge cases.

Show
Andrew Jaquith added a comment - 11/Jul/08 8:26 AM I've attached a patch for this that fixes the issue. It adds a separator() method to the UrlBinding annotation (which defaults to "&"), and an equivalent getSeparator() method to the UrlBinding class. It also adds some processing logic to UrlBindingFactory to properly recognize when a clean URL that matches a prototype might have appended parameters. The patch includes a CleanUrlBindingTests file that exercises several edge cases.

People

Vote (13)
Watch (11)

Dates

  • Created:
    02/Sep/06 9:48 AM
    Updated:
    04/Jan/11 3:10 PM
    Resolved:
    18/Jul/07 9:18 PM