grails 2.3.8 here. When i g:link to certain controller actions, i want to put a namespace in the url in front of the controller name.
For example:
call "app/apple/eat" -> "app/admin/apple/eat"
Since there are many dynamic and static scaffolded controllers involved, I thought i can do this with some UrlMappings expression but i dont know how to do this.
I tried out something like this without no success:
static mappings = {
"/apple/$action?/$id?" (redirect:"/admin/apple/$action?/$id?")
}
using namespace = "admin" in the AppleController doesnt work aswell
thanks for advices
For the example
call "app/apple/eat" -> "app/admin/apple/eat"
you could do it via the grails-app/conf/UrlMappings.groovy with the following configuration (which works than for the whole application):
class UrlMappings {
static mappings = {
"/admin/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
with this configuration, your controllers are reachable via:
http://localhost:8080/apple/admin/apple/eat/1
etc.
Or!
If you would only have apple under the admin prefix, than you could do the following in grails-app/conf/UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/admin/apple/$action?/$id?"(controller : "apple")
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Or!
If you have more redirects, you could group them together:
static mappings = {
group("/admin") {
"/apple/$action?/$id?(.${format})?"(controller: 'apple')
"/peach/$action?/$id?(.${format})?"(controller: 'peach')
}
...
Related
In grails, supposing you have a project named 'MainProject', my default index is http://localhost:8080/MainProject/ and the page associated with this exact url is views/index.gsp.
I would like the starting link of the project NOT to be http://localhost:8080/MainProject/ but something like http://localhost:8080/MainProject/users/login.
I tried to edit the URL Mappings from this:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view: '/index')
"500"(view:'/error')
}
}
to this:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view: '/users/login')
"500"(view:'/error')
}
}
After the above change, by starting the project, the url remains as http://localhost:8080/MainProject/, but the page shown is not views/index.gsp
but views/users/login.gsp. The gsp is correctly rendered, but the url is still not the one I need.
How to solve this?
You can do this with:
"/"(redirect: '/users/login')
Also see Grails URL Mapping Documentation
My grails project has at least 20 controllers each with at least 4 methods each, and it is continuing to grow. I decided to annotate every method like this:
import enums.URLMapped
class HomeController {
#URLMapped(url="/", alias="home")
def landingPage() {
render(view: "/index")
}
}
How can I append this URL value inside the URLMapping.groovy dynamically? I could do something like:
import enums.URLMapped
import java.lang.reflect.Method
import org.apache.commons.lang.StringUtils
import org.codehaus.groovy.grails.commons.DefaultGrailsControllerClass
import org.codehaus.groovy.grails.commons.GrailsApplication
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
// My annotation crawler/parser
for(DefaultGrailsControllerClass controller in GrailsApplication.getControllerClasses()) {
for(Method method in controller.getClazz().getDeclaredMethods()) {
if(!method.isSynthetic()) {
if(method.isAnnotationPresent(URLMapped.class)) {
URLMapped url = method.getAnnotation(URLMapped.class)
name "${url.alias()}": "${url.url()}" {
action="${method.getName()}"
controller="${StringUtils.capitalize(controller.getClazz().getSimpleName().split(/Controller/).getAt(0)}"
}
/* What I want to achieve above is this static URL:
name home: "/" {
action="landingPage"
controller="Home"
}
*/
}
}
}
}
}
}
But the problem is that static mapping is a closure and you shouldn't suppose to loop (?) inside it. So how can I add my URL? I could always put all static URL for all of our links here, it's just getting tedious to maintain.
I would strongly recommend you get rid of this idea with annotations but to divide your project into several plugins (each - a separate grails-app) tied together with gradle.
Some useful info:
https://docs.gradle.org/current/userguide/custom_plugins.html
https://docs.gradle.org/current/userguide/multi_project_builds.html
I had this idea that I wanted to assign prefixes to my two categories of URLs. One category would be
/baseurl/api/$controller
for secured REST URLs and then:
/baseurl/register/$action
for the current one category of unsecured URLs
I then thought it would be good to store the prefixes /api and /register in variables so they could be referenced elsewhere if need be (such as my security configuration). However, I can't get this to work in my UrlMappings. None of the variables defining the URL parts seem to be populated. Consider the following UrlMappings.groovy where I've defined a SignupController.groovy to map to registration, and every other controller is API related:
class UrlMappings {
static final API_URL_ROOT = "/api"
static final REGISTER_URL_ROOT = "/register"
static mappings = {
API_URL_ROOT + "/$controller/$action?/$id?(.$format)?" {
constraints {
}
}
REGISTER_URL_ROOT (controller: "signup")
"/"(view: "/index")
"500"(view: '/error')
"404"(view: '/notFound')
}
}
When this runs I can only invoke my controllers through the root URL directly, ie, localhost:8080/login and localhost:8080/signup whereas I want it to be:
localhost:8080/api/v1/login and localhost:8080/register/signup
How do I fix this?
You could use group to separate your different api
http://mrhaki.blogspot.com/2013/11/grails-goodness-grouping-url-mappings.html?m=1
In your case it could become something like that
group("/api") {
"/$controller/$action?/$id?(.$format)?" { constraints { } }
// PUT HERE ALL OTHER API MAPPING
}
group("/register") {
"/$controller" {}
// PUT HERE ALL REGISTER MAPPINGS
}
If you need to take into consideration a version for your api just do this
group("/api") {
"/$namespace/$controller/$action/$id?" {}
// OR ANY OTHER MAPPING YOU NEED. Then in your controller define
// static namespace = 'v1'
// for example for your v1 controllers.
}
Then you can declare you groups as variables if you want.
static API="/api"
group (API) { .... }
Hope that helps if not sorry for the noise
I know it is possible to do something like this:
Controller foo with action bar can be accessed by (1):
/appname/foo/bar
And it can be rewritten using URL mappings - e.g like this:
"/foobar/foo/$action"(controller: "foo")
And then access it via (2):
/appname/foobar/foo/bar
But it is still possible to access it via (1). This is of course because of the default URL mapping:
"/$controller/$action?/$id?"()
But I would rather not delete this, because this basically means I have to manually write mappings to every other controller/action that follows the default pattern.
It is possible to obtain url-patterns for specific controller/actions like (2) without using URL mappings? If not, is there an easy way to "exclude" controllers from the default mapping closure?
The Solution is to change the default mapping, in a way to exclude the whished special controller URL.
class UrlMappings {
static myExcludes = ["foo"]
static mappings = {
"/foobar/foo/$action"(controller: "foo") // your special Mapping
// the rewritten default mapping rule
"/$aController/$aAction?/$id?"{
controller = { (params.aController in UrlMappings.myExcludes) ? "error" : params.aController }
action = { (params.aController in UrlMappings.myExcludes) ? "notFound" : params.aAction }
constraints {
// apply constraints here
}
}
}
}
For the rewritten default rule you have to prevent usage of default variable names $controller and $action. Instead of error/notFound you are also able to redirect to other locations.
If you can make your rule breaking scenario more specific than the Grails default $controller/$action?$id? pattern, then the default can be left as is and will apply to everything outside of your exception pattern. I made a quick Person domain and performed a generate-all. Then I made a BreakRuleController just by itself.
class UrlMappings {
static mappings = {
"/b/$action?/$someVariable?"(controller: "breakRule")
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
With this UrlMapping, if you access the URI "/b/foo/stackoverflow" it will print "Breaking Rules foo: stackoverflow". If you go to "/b", it will print "Breaking Rules index".
Then if you go to the standard Person URI's, all your default Grails scaffolding works fine also (create, edit, etc.) because that gets mapped to the typical "$controller/$action?/$id?" pattern.
class BreakRuleController {
def index() {
print "Breaking Rules index"
}
def foo(String someVariable) {
print "Breaking Rules foo: " + someVariable
}
}
For some reason I get a huge list of errors when using following code:
class UrlMappings {
static grailsApplication
static mappings = {
grailsApplication.controllerClasses.each { controllerClass -> // FAILS!
println(controllerClass.name)
}
"/$controller/$action?/$id?"{}
"/"(view:"/index")
"500"(view:'/error')
}
Errors: http://pastebin.com/tiEsENie
Where as following code works just fine and prints all the controller names:
class UrlMappings {
static grailsApplication
static mappings = {
"/$controller/$action?/$id?"{
grailsApplication.controllerClasses.each { controllerClass -> // WORKS!
println(controllerClass.name)
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Isn't it possible to access the static grailsApplication from inside static mappings?
(I need to be able to get the controller names in order to dynamically create urlmappings)
While ApplicationHolder still works, the grails docs state this for the in the deprecation comments
deprecated: Use dependency injection or implement GrailsApplicationAware instead
Since grailsUrlMappingsHolderBean implements GrailsApplicationAware, I found that the code below works in 2.0 as well
class UrlMappings {
static mappings = {
getGrailsApplication().controllerClasses.each{ controllerClass ->
if(controllerClass.name./*your logic here*/){
"/mod/action" {
controller = "${controllerClass.name}"
}
}
}
}
}