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
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
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')
}
...
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
Grails 2.4.x here.
I have a requirement that all the methods of all my Grails services, generated by grails create-service <xyz>, be "wrapped"/intercepted with the following logic:
try {
executeTheMethod()
} catch(MyAppException maExc) {
log.error(ExceptionUtils.getStackTrace(maExc))
myAppExceptionHandler.handleOrRethrow(maExc)
}
Where:
log.error(...) is the SLF4J-provided logger you get when you annotate your class with the #Slf4j annotation; and
ExceptionUtils is the one from org.apache.commons:commons-lang3:3.4; and
myAppExceptionHandler is of type com.example.myapp.MyAppExceptionHandler; and
This behavior exists (or has the option to exist in the event that it needs to be explicitly called somehow) for each method defined in a Grails service
So obviously this wrapper code needs to include import statements for those classes as well.
So for example if I have a WidgetService that looks like this:
class WidgetService {
WidgetDataService widgetDataService = new WidgetDataService()
Widget getWidgetById(Long widgetId) {
List<Widget> widgets = widgetDataService.getAllWidgets()
widgets.each {
if(it.id.equals(widgetId)) {
return it
}
}
return null
}
}
Then after this Groovy/Grails/closure magic occurs I need the code to behave as if I had written it like:
import groovy.util.logging.Slf4j
import org.apache.commons.lang3.exception.ExceptionUtils
import com.example.myapp.MyAppExceptionHandler
#Slf4j
class WidgetService {
WidgetDataService widgetDataService = new WidgetDataService()
MyAppExceptionHandler myAppExceptionHandler = new MyAppExceptionHandler()
Widget getWidgetById(Long widgetId) {
try {
List<Widget> widgets = widgetDataService.getAllWidgets()
widgets.each {
if(it.id.equals(widgetId)) {
return it
}
}
return null
} catch(MyAppException maExc) {
log.error(ExceptionUtils.getStackTrace(maExc))
myAppExceptionHandler.handleOrRethrow(maExc)
}
}
}
Any ideas as to how I might be able to achieve this? I'm worried that a pure Groovy closure might interfere somehow with whatever Grails is doing to its services under the hood at runtime (since they are all classes that don't explicitly extend a parent class).
Here is what I was trying to pin point in my comment:
package com.example
import groovy.util.logging.Log4j
#Log4j
trait SomeTrait {
def withErrorHandler(Closure clos) {
try {
clos()
} catch(Exception e) {
log.error e.message
throw new ApplicationSpecificException(
"Application Specific Message: ${e.message}"
)
}
}
}
Service class:
package com.example
class SampleService implements SomeTrait {
def throwingException() {
withErrorHandler {
throw new Exception("I am an exception")
}
}
def notThrowingException() {
withErrorHandler {
println "foo bar"
}
}
}
Test:
package com.example
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(SampleService)
class SampleServiceSpec extends Specification {
void "test something"() {
when:
service.throwingException()
then:
ApplicationSpecificException e = thrown(ApplicationSpecificException)
e.message == "Application Specific Message: I am an exception"
}
void "test something again"() {
when:
service.notThrowingException()
then:
notThrown(Exception)
}
}
Here is the sample app.
Grails 3.0.9 but it should not matter. this is applicable for Grails 2.4.*
You can intercept the calls to your Service class methods either using MetaInjection or Spring AOP. So you don't have to write closure in each Service class. You can look into this blog that explains both the approaches with examples.
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}"
}
}
}
}
}