Creating a Grails URLMapping for all methods in a controller? - grails

How do I create a urlmapping that maps controller A essentially as controller B?
I thought something like this would work, but no dice.
"/my/"(controller: 'other', action: '*')

When you specify a controller in UrlMappings, leave off the trailing "Controller" (e.g. "my", instead of "myController"). You also need some way of choosing which action.
You probably want something like "/my/$action?"(controller: 'my'), which maps urls like /my/foo to the foo action in MyController. The trailing question mark means the action part of the url is optional; /my will trigger MyController.index.
Note that the grails convention is already to map /my to MyController with the default mapping "/$controller/$action?/$id?"{}, so you don't need a special UrlMapping for your example. You might want to consider just using the defaults and follow the convention.

Related

Is there a better way to distinguish between string id parameters and action names in my routes?

Problem
I have a TagsController, with the standard CRUD Actions. But for the Details result I would rather not include the action name in the url. I want URLs like this:
tags
tags/my-tag-url //no action name specified
tags/create
tags/edit/123
... and as normal ...
But obviously, there is no way for the routing to distinguish between an action name and the my-tag-url parameter.
Possible Solution 1
Create 2 routes. One for the tags/{my-tag-url} route, and another for the tags/{action}/{id} routes.
Downside: The requirement to adorn one of the routes with a constraint - either checking for the existince of my-tag-url in the DB, or checking against a list of the action names on the controller.
(BTW - is either of these prefereable over the other?)
Possible Solution 2
Change tags/my-tag-url to tag/my-tag-url (notice singular) and map this to a different controller.
Downside: The urls are no longer 'hackable'. I can't just remove the my-tag-url to get to the index page listing all tags.
But obviously, there is no way for the routing to distinguish between
an action name and the my-tag-url parameter.
Constraints are the means to achieve this.
The requirement to adorn one of the routes with a constraint - either
checking for the existince of my-tag-url in the DB, or checking
against a list of the action names on the controller.
You've given yourself an either-or situation but you can also (preferably) use the regex option so no lookup is required:
routes.MapRoute("blog", "{year}/{month}/{day}"
, new {controller="blog", action="index"}
, new {year=#"\d{4}", month=#"\d{2}", day=#"\d{2}"});
In your case:
routes.MapRoute("tagurl", "{tagurl}"
, new {controller="tags", action="TagRedirector"}
, new {tagurl=#"#"^htt\w*"});
public ActionResult TagRedirector(string tagurl)
{
..
}
This will likely run into the dangerous request territory however, so if your tags have no pattern (www.)? that you can filter for then you could just as easily use ! operator in your regex pattern to knockout the names of action methods on your controller that should be filtered out i.e. only use this route if {tagsurl} is not "index,other, whatever"

Grails using spring security annotations and UrlMapping to secure controllers

I am trying to secure a controller:
#Secured(['ROLE_ADMIN'])
class FooBarController {
}
I have the controller mapped in UrlMappings:
"/foo/bar"
It seems that when I try and access the controller as /foo/bar the annotations are effectively ignored.
I saw something that said I need to use the controllerAnnotations.staticRules. My first question is:
Do I need to duplicate all my rules in the static rules or is it enough to say it's secured and the filters will pick up the specific rules from the annotations?
I have another scenario where I secure a contoller with UrlMappings, the default URL path is changed but not the controller name e.g.
#Secured(['ROLE_ADMIN'])
MyApiController {
}
UrlMapping is: /api/company/1/myApi
In this case the annotation is picked up without any necessary configuration in the staticRules so I'm pretty confused by what needs to be configured where and under what circumstances.
It looks like the problem here is that the UrlMapping doesn't match the default controller mapping convention. i.e. the Config.groovy mapping is referring to the UrlMapping instead of referring to the actual controller name.
See this answer: https://stackoverflow.com/a/16737980/57357 for a fuller explanation.
IMO this is non-intuitive, but that's the way it currently works. Note that it does not work this way if you use the InterceptUrlMap style of configuration. If you use the InterceptorUrlMap style, you do refer to the UrlMappings.groovy's URL mappings.

UrlMapping :: View attribute in matched block ignored

Given URI /admin/article/index, why would this url mapping not work?
"/admin/$controller/$action?/$id?"{
view = "/admin/index" // no dice, ignored
//action = "foo" // uncommented, this is picked up
}
I'd like for all admin controllers to use the admin view by default (and not have to render the view in each action of each controller). Same goes for "/account/$controller/..." and any other site module that should use a common view.
Perhaps there's another way to achieve this, but assumed that UrlMappings is the place to do it...
Looks like you are trying to do something very different than what you wrote.
You already have the action mapped in the base URL mapping, and the view is automatically selected based on the controller, so you need to define different mappings for those views that don't have a controller, and yet another mapping for items with a default action of foo. The default action on controllers is index, though, so there is usually no need to supply a default action without also specifying a controller.
I think you are, in general, misunderstanding how an MVC framework works. The controller should not be rendering anything, and the views should be specific to the controller/action. If multiple controllers are rendering the exact same view, I'd be willing to bet that either the controller is rendering HTML or the view is overly complicated.
You should look into Layouts with SiteMesh, which allows you to create default template structures, then just have the specific content change through views.

URL Mapping prefix in Grails

Recently, I'm trying to migrating my application from CakePHP to Grails. So far it's been a smooth sailing, everything I can do with CakePHP, I can do it with much less code in Grails. However, I have one question :
In CakePHP, there's an URL Prefix feature that enables you to give prefix to a certain action url, for example, if I have these actions in my controller :
PostController
admin_add
admin_edit
admin_delete
I can simply access it from the URL :
mysite/admin/post/add
mysite/admin/post/edit/1
mysite/admin/post/delete/2
instead of:
mysite/post/admin_add
mysite/post/admin_edit/1
mysite/post/admin_delete/2
Is there anyway to do this in Grails, or at least alternative of doing this?
Grails URL Mappings documentation doesn't help you in this particular case (amra, next time try it yourself and post an answer only if it's any help). Daniel's solution was close, but wouldn't work, because:
the action part must be in a closure when created dynamically
all named parameters excluding "controller", "action" and "id" are accessible via the params object
A solution could look like this:
"/admin/$controller/$adminAction?/$param?"{
action = { "admin_${params.adminAction}" }
}
The key is NOT to name the parameter as "action", because it seems to be directly mapped to an action and can not be overriden.
I also tried a dynamic solution with generic prefixes and it seems to work as well:
"/$prefix/$controller/$adminAction?/$param?"{
action = { "${params.prefix}_${params.adminAction}" }
}
I didn't test it, but try this:
"mysite/$prefix/$controller/$method/$id?"{
action = "${prefix}_${method}"
}
It constructs the action name from the prefix and the method.
Just take a look on grails URL Mappings documentation part

Create a Route Constraint that only applies a route when the action has a particular action filter

I have a list of actions on various controllers that are 'Admin' functions (create, update, delete) but other actions on those same controllers that aren't admin actions.
What I want to do is create a route that will prefix /Admin/ before all urls that call an action that have the Authorize filter attribute.
Is this even possible to do?
Yes everything is possible, but I think what you mean to say is it easy to do? And the answer is no. What you have to do is create your own route, and then add this customized route to the route mapping. This isn't hard to do, but the problem arises in that the routes are initialized before the controller, so you will have to handle the lookup and reflection yourself to check for your criteria.
There is an alternative option, you can try to use the ActionMethodSelectorAttribute which allows you to create custom selectors for your Action Methods and ignore ones that don't contain the Authorize attribute. An example of this attribute being used is the ActionVerbAttribute.
The easiest way by far is to just create a custom extension for the Html.ActionLink that does its own checks and keep it as a display only thing, and then create dual routes for the same controller in your Global.asax.

Resources