Grails 2.5.0 UrlMappings with two consecutive slashes - grails

I have inherited a Grails 2.2.4 UrlMappings (partially reproduced below) that I am trying to upgrade to 2.5.0.
Named mappings api0 & api2 work, but the two consecutive slashes in api1 (between $controller & $id) do not seem to be matched properly in 2.5.0 (though they were matched in 2.2.4). e.g.:
PUT /api/ticket//123.json
returns a 403, despite my controller allowing PUTs for its update action.
name api0: "/api/$controller/$id?(.$format)?" {
action = [GET: 'show', PUT: 'update', POST: 'save', DELETE: 'delete']
constraints {
id(matches: /\d+/)
}
}
name api1: "/api/$controller//$id?(.$format)?" {
action = [GET: 'show', PUT: 'update', POST: 'save', DELETE: 'delete']
constraints {
id(matches: /\d+/)
}
}
name api2: "/api/$controller/$action/$id?(.$format)?" {
constraints {
id(matches: /\d+/)
}
}
I can't change the incoming URLs and/or HTTP methods (and I didn't create them), so please don't tell me to change the clients to replace the double slashes in the URLs with single slashes, or that the URLs and/or HTTP methods don't follow REST paradigms.

Related

Grails 4.1 - Static Mapping in URL Mappings not working properly after upgrade

I am currently upgrading to Grails 4.1. In the past version I had a static mapping in my URL Mappings.groovy as follows:
class UrlMappings {
static mappings = {
name tool: "tool/$controller/$action?/$id?"{
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
}
"/"(controller: "auth", action: "login")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
This was working perfectly in previous Grails versions and when I click on a link to redirect to the url localhost:8000/tool/converters/list, converter would be recognized as the Controller, list would be recognized as the Action and the correct view would be displayed. Now that I have upgraded, when I click on the link, the url that it redirect to is localhost:8080/tool%2Fconverters/list and the error message "This page isn't working" is what is displayed in the view. The "%2F" somehow gets inserted into the url and is causing the page to not display.
I have looked at the Grails 4 documentation and I don't see any indication that the format for static mappings in the URL Mappings has changed. Does anyone have any idea as to why this is happening and how I can fix it?
See the project at https://github.com/jeffbrown/rookycodermapping.
https://github.com/jeffbrown/rookycodermapping/blob/0b7ff27a7fc8c1c1f7b4cf3dc14430ca1cac7be5/grails-app/controllers/rookycodermapping/SchemaController.groovy
package rookycodermapping
class SchemaController {
def show() {
render 'This is being rendered by the show action in SchemaController.'
}
}
https://github.com/jeffbrown/rookycodermapping/blob/0b7ff27a7fc8c1c1f7b4cf3dc14430ca1cac7be5/grails-app/controllers/rookycodermapping/UrlMappings.groovy
package rookycodermapping
class UrlMappings {
static mappings = {
name tool: "/tool/$controller/$action?/$id?" {}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
https://github.com/jeffbrown/rookycodermapping/blob/0b7ff27a7fc8c1c1f7b4cf3dc14430ca1cac7be5/grails-app/views/index.gsp#L56-L59
<p>
Click <g:link action="show" controller="schema">here</g:link> to invoke the show action (g:link action="show" controller="schema").
Click <g:link uri="/tool/schema/show">here</g:link> to invoke the show action (g:link uri="/tool/schema/show").
</p>
Both of those links appear to work as expected.

Grails: URL parameter in url mappings not interpreted as expected

I'm writing a REST API with grails and need to put a variable on the path of a method. The URL to call this method (which is delete) will look like:
http://localhost:8080/profiles/576/gameLibrary/591
I have the following in my URLMappings class:
"/gameLibrary/${inventoryId}/"(controller: "profile", action: "removeFromLibrary", method: "DELETE")
I tried variations for the mapping like "$inventory" without the curly brackets and leaving off the trailing slash. When I attempt to call this URL, I get a 404. I've created the URL report, and I see the following for the mapping in question:
Controller: profile
| DELETE | /profiles/${profileId}/gameLibrary/null | Action: removeFromLibrary |
So, it's like it's trying to interpolate "${inventoryId} instead of using it as a path variable.
Perhaps I'm misunderstanding how to create path variables. I thought this section of the documentation on embedded variables was describing the approach I took: http://docs.grails.org/latest/guide/theWebLayer.html#embeddedVariables.
Just in case it helps, posting my whole UrlMappings class:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/games"(resources:"game")
"/gameGenres"(resources:"gameGenre")
"/siteUsers"(resources:"siteUser")
"/profiles"(resources:"profile") {
"/gameLibrary" (controller: "profile", action: "getGameLibrary", method: "GET")
"/gameLibrary" (controller: "profile", action: "addGameToLibrary", method: "PUT")
"/gameLibrary/${inventoryId}/"(controller: "profile", action: "removeFromLibrary", method: "DELETE")
"/wishList" (controller: "profile", action: "getWishList", method: "GET")
}
"/inventoryItems"(resources:"inventoryItem")
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
Any help would be appreciated greatly.

Grails not correct redirect

I have a Grails 2.3.4 webapp and when I try to go to an url, for example:
http://localhost:8080/${myapp}/
it always redirects correct how I wanted, but if:
http://localhost:8080/${myapp}
without slash I get 404 error(Page not found).
my UrlMappings class:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
//"/"(view: '/error404')
"/"(action: "someAction", controller: "someController")
"404"(view: '/error404')
"500"(view:'/error500')
}
}
What I have to do with this?

Does Grails supports Restful nested URLs?

Does Grails supports Restful nested URLs like '/articles/1/comments/5' by default? If not is there any plugin for that?
See here. Nested RESTful URLs will be supported in Grails 2.3.
If you're bound to an older release of Grails (e.g < 2.3), and the available plugins don't work out, you can use named URL mappings to produce an effective restful mapping.
Here's an example from a project of mine - I've left out some details, but hopefully this gets you started if you decide to give this approach a try.
In your UrlMappings.groovy
/** 1. Mappings can handle multiple actions depending on HTTP
method like Rest. Names are a little clunky, like this would
be more appropriate as "resource" vs "showResource" but we didn't want
potential naming conflict in future release
2. TODO: DRY constraints - make constraints global
3. make sure controllers have proper actions defined
*/
/** RESTFUL mapping for single resource */
name listResources: "/$controller" {
action = [GET: "list", POST: "save"]
}
name createResource: "/$controller/create" {
action = [GET: "create" ]
}
name deleteResource: "/$controller/$id?/delete" {
action = [POST: "delete", DELETE: "delete"]
constraints { id(matches: /[0-9]+/) }
}
name editResource: "/$controller/$id?/edit" {
action = [GET: "edit", PUT: "update", POST: "update"]
constraints { id(matches: /[0-9]+/) }
}
name showResource: "/$controller/$id?" {
action = [GET: "show", PUT: "update", POST: "update", DELETE: "delete"]
constraints { id(matches: /[0-9]+/) }
}
/** RESTFUL mapping for CHILD with PARENT */
name listChildResources: "/$parentResource/$pid/$controller" {
action = [GET: "list", POST: "save"]
constraints { pid(matches: /[0-9]+/) }
}
name createChildResource: "/$parentResource/$pid/$controller/create" {
action = [GET: "create" ]
constraints { pid(matches: /[0-9]+/) }
}
name showChildResource: "/$parentResource/$pid/$controller/$id?" {
action = [GET: "show", PUT: "update", POST: "update", DELETE: "delete"]
constraints {
id(matches: /[0-9]+/)
pid(matches: /[0-9]+/)
}
}
name editChildResource: "/$parentResource/$pid/$controller/$id?/edit" {
action = [GET: "edit"]
constraints {
id(matches: /[0-9]+/)
pid(matches: /[0-9]+/)
}
}
Make sure you controllers have actions and supported HTTP methods define, eg
static allowedMethods = [
save: "POST",
update: ["POST", "PUT"],
delete: ["POST", "DELETE"]
]
Then use the mappings like so (for example lets say we have Gardens and Plants as resources).
//show a garden
<g:link mapping="showResource" controller="garden"
id="${gardenInstance.id}">${gardenInstance.name}</g:link>
//create a plant for garden
<g:link mapping="createChildResource" controller="plant"
params="[parentResource: 'garden', pid: gardenInstance.id]">Add Plant</g:link>
//show list of plants within a garden
<g:link mapping="listChildResources" controller="plant"
params="[parentResource: 'garden', pid: gardenInstance.id]">List plants for Garden</g:link>
Shown here it's pretty verbose, but you could put all of this into TagLib and have something like.
<g:restShow resource="garden"
id="${gardenInstance.id}">${gardenInstance.name}</g:restShow>
<g:restCreate" resource="plant"
parent="${gardenInstance}">Add Plant</g:restCreate>

Grails UrlMapping Redirect to keep DRY

I am working with Grails 2.1.1 and would like to add a handful of customized URLs that map to Controller Actions.
I can do that, but the original mapping still works.
For example, I created a mapping add-property-to-directory in my UrlMappings as follows:
class UrlMappings {
static mappings = {
"/add-property-to-directory"(controller: "property", action: "create")
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Now, I can hit /mysite/add-property-to-directory and it will execute PropertyController.create, as I would expect.
However, I can still hit /mysite/property/create, and it will execute the same PropertyController.create method.
In the spirit of DRY, I would like to do a 301 Redirect from /mysite/property/create to /mysite/add-property-to-directory.
I could not find a way to do this in UrlMappings.groovy. Does anyone know of a way I can accomplish this in Grails?
Thank you very much!
UPDATE
Here is the solution that I implemented, based on Tom's answer:
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/add-property-to-directory"(controller: "property", action: "create")
"/property/create" {
controller = "redirect"
destination = "/add-property-to-directory"
}
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
RedirectController.groovy
class RedirectController {
def index() {
redirect(url: params.destination, permanent: true)
}
}
It is possible to achieve this:
"/$controller/$action?/$id?" (
controller: 'myRedirectControlller', action: 'myRedirectAction', params:[ controller: $controller, action: $action, id: $id ]
)
"/user/list" ( controller:'user', action:'list' )
and in the action you get the values normallny in params:
log.trace 'myRedirectController.myRedirectAction: ' + params.controller + ', ' + params.action + ', ' + params.id
As of Grails 2.3, it is possible to do redirects directly in the UrlMappings, without the need for a redirect controller. So in case you ever upgrade, you can redirect in UrlMappings like so, as per the documentation:
"/property/create"(redirect: '/add-property-to-directory')
Request parameters that were part of the original request will be included in the redirect.
In recent Grails version (currently 5.x) you need to pass a Map into the redirect: property:
"/viewBooks"(redirect: [uri: '/add-property-to-directory'])
https://docs.grails.org/latest/guide/theWebLayer.html#redirectMappings

Resources