URL Mapping prefix in Grails - 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

Related

Grails <g:link> rewriting links to match UrlMappings instead of controller/action

Currently using grails 4.0.3.
I'm trying to generate a link to a controller action using a simple <g:link> tag:
<g:link controller="myController" action="myAction" params="[someParam:'myValue']">Link text</g:link>
In my UrlMappings file, I have this controller mapped to a common URL for external calls. This mapping forces some parameters that I want forced when coming to this mapping:
class UrlMappings {
static mappings = {
"/service/someExposedUrl" {
controller = "myController"
action = "myAction"
someParam = "defaultValue"
}
}
}
However, the link that gets written to the page in my application gets written using the UrlMapping definition.
http://serverUrl/service/someExposedUrl?someParam=<ignored>
instead of
http://serverUrl/myController/myAction?someParam=myValue
In this case, the parameters are ignored on the /service URL because they're hard-coded in the UrlMapping.
Is there a way to force grails to link to the specified controller/action instead of the mapping?
Up front caveat: I have not tested this for this specific situation, though I have used these pieces individually. Second caveat: I would not be remotely surprised to learn there's an even better way to do this...grails has a lot of options sometimes!
You should be able to create a named mapping in your UrlMappings, then reference that in your createLink.
UrlMappings:
name arbitraryName: "/myController/myAction" {
controller = "myController"
action = "myAction"
}
Link:
<g:link mapping="arbitraryName" params="[someParam:'myValue']">Link text</g:link>
You may be able to get what you need with a combination of Custom URL mapping and the exclude keyword in the original URLMappings.
Create MyControllerUrlMappings. This guide gives several examples of different ways of writing them out. I found it helpful.
class MyControllerUrlMappings {
static mappings = {
// Url Mappings specific only to this controller
}
}
https://guides.grails.org/grails_url_mappings/guide/index.html#_multiple_urlmappings_files
Your app will still be running the original UrlMappings file though. So you'll have the new custom Url mappings and the default ones. To fix that you can exclude your controller and it's methods in the original UrlMappings file.
class UrlMappings {
static excludes = [
"/myController",
"/myController/*"
]
static mappings = {...}
...
Not as elegant as I'd like, but in a similar situation this is how I got it working.

Grails Reverse Url Mapping: Is there a way to build links based on the currently matched route?

I'm trying to build some dynamic URL mappings that are based on the domain classes of the grails project. The goal is to use a single generic controller, but the URL decides which domain is being used for processing. The problem is now, that I don't get the desired URLs when using the <g:link /> tag.
I tried the following variants to create the URL mappings:
static mappings = {
Holders.grailsApplication.domainClasses.each {
"/admin/${it.propertyName}/$action?/$id?"(controller: "admin")
}
}
static mappings = {
"/admin/$domainClass/$action?/$id?"(controller: "admin")
}
Both variants work for the actual URL matching. But I personally don't like the behavior of the reverse URL mapping by grails. In case of variant 1, the reverse mapping always resolves to the last added URL mapping for the AdminController. For case 2 I have the problem that I would have to pass the domainClass-Parameter to every link-creating call, even though it is theoretically not necessary, since the information is already present in the current request.
I know there is the possibility of using named URL mappings and then using something like <g:link mapping="mapping_name" />. The problem is that I am using some generic application-wide partial-views, where I try to only provide the necessary information for creating a link, like <g:link action="something" />.
This leads to my two questions:
Is there a possibility to get g:link to build the URL based on the matched mapping in the current request?
Is there a way to get a reference to the mapping that matched the current request, so I can implement to desired behavior myself?
You could define named mappings like
Holders.grailsApplication.domainClasses.each { dc ->
name((dc.propertyName):"/admin/${dc.propertyName}/$action?/$id?" {
controller = "admin"
domainClass = dc.propertyName
})
}
With the mapping name saved in the params you can now do
<g:link mapping="${params.domainClass}">link text</g:link>

How to create canocical from controller in grails?

In grails, I have a link like /myapp/questions/all
The all is a parameter (all, replied, ...) passed to my controller.
I have a form to search question depending of type : in all, in replied, ...
In the search form, I have an hidden field to pass parameter.
But the url displayed is /myapp/questions/ ans not /myapp/questions/all
So I tried with url : url="[action:'question', controller:'mycontroller', params:['monparam':'${mavariable}']]"
but it's not working.
Any idea ?
Thanks
You can do it like this:
class UrlMappings {
static mappings = {
name nameOfTheMapping: "/question/$para/" {
controller = "mycontroller"
action = "question"
}
...
Then you can access the mapping by:
<a href='${createLink(mapping: 'nameOfTheMapping', params: [para: para.encodeAsUrl()])}' title='test'>Test</a>
The above code is created in my taglib, so it maybe a little different if you want to use it in a view.
I don't completely understand your question, but it seems you are not following the grails convention. the url is of the form
/app/controller/action
so grails is interpreting the 'all' part of your url as the action to invoke on the questions controller (what I got from your 'link like /myapp/questions/all').
Where I got confused was with your url specification.
url="[action:'question', controller:'mycontroller', params:['monparam':'${mavariable}']]"
Based on that, you should have a controller called 'mycontroller', with an action called 'question' on it. The url you will see in the browser would be
/app/mycontroller/question?monparam:whatever
See here for details on controllers in general.
You need to edit grails-app/conf/UrlMappings.groovy and create a mapping to the controller that omits the action. (since you're handling this all within one action)
something like
"/questions/$question_type" (controller: 'questions', action: 'your_action')
where "your_action" is the name of the action that is processing these requests.
Then in QuestionsController.groovy:
def your_action = {
// use question_type as needed
def questions = Questions.findByQuestionType(params.question_type)
// etc.
}
You can do a variety of things to affect the mapping of urls to requests, check out the UrlMapping section in the Grails User Guide.

another grails urlmapping question

ok, so i asked, and got an answer on how to make a single controller instance case-insensitive vis-a-vis urls. I can do
"/mycontroller/$action?/$id?"(controller: "myController")
so when an app outside tries to reference link in our app, their lowercase urls ( :( sigh ) will work.
I need to extend this to include actions as well. So the question is, following the above approach, do i need put a url mapping in for each action?
/mycontroller/methodone/(controller: "myController", action: methodOne)
/mycontroller/methodtwo/(controller: "myController", action: methodTwo)
Something like the above?
This is similar to the question below which I have answered and included source code
How to make my URL mapping case insensitive?
You can use a closure to calculate the action (or other parameters) programmatically:
"/mycontroller/$a?/$id?" {
controller = 'myController'
action = { params.a?.toLowerCase() }
}

url mappings grails

right, basically we have a link into our system that is all lowercase but that needs to be camel cased. rather than get the other developers to fix this, I want to just make the problem go away by url magic
"/myobj/$action?/$id?"{
controller: "myObj"
}
what i am trying to do is map anything that references controller myobj to controller myObj instead, and keep the action and everything else the same. The above code gives an exception, saying the
2010-07-11 18:14:50,021 ERROR [default] - Servlet.service() for servlet default threw exception
java.lang.IllegalArgumentException: URL mapping must either provide a controller or view name to map to!
however the following works
"/myobj/$action?/$id?"(controller: "myObj")
I dont understand, given the documentation. Why didnt the closure work? This is grails 1.2.0...
You should use
'/myobj/$action?/$id?' {
controller = 'myObj'
}
Edit
As a bit of explanation: the colon in the working example creates a map as described here (the working case is a Groovy function invocation with named parameters). Within the closure, you need to set the value of the controller property directly, i.e., using the equals sign.
This difference is demonstrated not specifically highlighted in the Grails URL mapping documentation.
Edit
Here's an untested method that may yield case insensitive controller mappings... maybe.
'$myController/$action?/$id?' {
grailsApplication.getArtefacts('Controller').find { it.name.toLowerCase() == myController.name.toLowerCase }.with { controller = it.name }
}
I'm assuming here (which I haven't verified) that the Grails application is available within the MappingCapturingClosure in the DefaultMappingUrlEvaluator.

Resources