Change Grails REST format /controller/<id>/<action> - grails

I messed around with this a bit yesterday and failed miserably. I want to convert:
"/$controller/$action?/$id?"
To
#in psudo
"/$controller/$id?/$action?"
#ideal regex
"\/(\w+)(\/\d+)?(\/\w+)?"
The most obvious way failed "/$controller/$action?/$id?"
I can write the regex's to do it, but I am having trouble finding a way to using true regexs (I found RegexUrlMapping but could not find out how to use it), and also can't find documentation on how to assign a group to a variable.
My question is 2 parts:
How to I define a URL Resource with a true regex.
How to I bind a "group" to a variable. In other words if I define a regex, how do I bind it to a variable like $controller, $id, $action
I would also like to be able to support the .json notation /user/id.json
Other things I have tried, which I thought would work:
"/$controller$id?$action?"{
constraints {
controller(matches:/\w+/)
id(matches:/\/\d+/)
action(matches:/\/\w+/)
}
}
also tried:
"/$controller/$id?/$action?"{
constraints {
controller(matches:/\w+/)
id(matches:/\d+/)
action(matches:/\w+/)
}
}

The grails way to deal with this is to set
grails.mime.file.extensions = true
in Config.groovy. This will cause Grails to strip off the file extension before applying the URL mappings, but make it available for use by withFormat
def someAction() {
withFormat {
json {
render ([message:"hello"] as JSON)
}
xml {
render(contentType:'text/xml') {
//...
}
}
}
For this you'd just need a URL mapping of "$controller/$id?/$action?"
I'm not aware of any way to use regular expressions in the way you want in the URL mappings, but you could get a forward mapping working using the fact that you can specify closures for parameter values that get evaluated at runtime with access to the other params:
"$controller/$a?/$b?" {
action = { params.b ?: params.a }
id = { params.b ? params.a : null }
}
which says "if b is set then use that as the action and a as the id, otherwise use a as the action and set id to null". But this wouldn't give you a nice reverse mapping, i.e. createLink(controller:'foo', action:'bar', id:1) wouldn't generate anything sensible, you'd have to use createLink(controller:'foo', params:[a:1, b:'bar'])
Edit
A third possibility you could try is to combine the
"/$controller/$id/$action"{
constraints {
controller(matches:/\w+/)
id(matches:/\d+/)
action(matches:/\w+/)
}
}
mapping with a complementary
"/$controller/$action?"{
constraints {
controller(matches:/\w+/)
action(matches:/(?!\d+$)\w+/)
}
}
using negative lookahead to ensure the two mappings are disjoint.

Related

URL rewrite (rewrite rule) in grails

How to set url rewrite mechanism in groovy grails? Please let me know by step by step. Please help me.
Have used this is grails? This works similar to htaccess. They have a comparison on this. So, think, we should be able to write a generic rule, for the redirects. Could you please check and see.
"/**-spid-$PROP_ID" { //Property Details Page
namespace = "propertydetails"
controller = "Desktop"
action = "show"
constraints {
PROP_ID(nullable: false, blank: false, validator: { val, obj ->
if (!(PdValidatorUtil.ValidatePropId(val))) {
return false
}
})
}
}
I have used the above url mappings in my code. It represents for url of type www.aaa.com/washington-spid-per4566 it should call show method of DesktopController in propertyDetails namespace.Under constraints i specify the contraints on my propid variable i.e. format kinda things. follow the below links for references.
http://docs.grails.org/3.1.1/ref/Plug-ins/URL%20mappings.html
http://mrhaki.blogspot.in/2013/11/grails-goodness-grouping-url-mappings.html

grails reflection - Calling a dynamic tag

I know this is odd, but I'm trying to provide a pass through interface in a taglib that allows the caller to pass in any other tag to be displayed in a container with additional processing.
To this effect, I'm trying to dynamically call an arbitrary tag in an arbitrary namespace. This may be clearer by example.
GSP:
<myLib:myTag someProp="blah" anotherProp="blah2" size="80" namespace="g" tag="textField">
In my taglib, I'm trying to display the tag they pass.
Taglib:
def myTag = {
String id = //some processing, not specified by caller
attrs.put("id", id)
def namespace = attrs.remove("namespace")
def tag = attrs.remove("tag")
out << ?????
}
The problem comes after the out... I'm having trouble calling the tag. I've tried the following with the following errors
namespace.tag(attrs) //No signature of method: java.lang.String.tag()
namespace."${tag}"(attrs) //No signature of method: java.lang.String.textField()
"${namespace}"."${tag}"(attrs) //No signature of method: java.lang.String.textField()
This seems to work, but the method needs to support tags in other namespaces
g."${tag}"(attrs)
So the question is How can I use reflection to use a dynamically defined taglib?
I don't have them pass the fully formed tag in the body() because I need to interact with it in the taglib.
I believe the following works, I'm not completely certain this is the best answer, so will leave the question open for suggestions from others.
String namespace = attrs.remove("namespace")
String tag = attrs.remove("tag")
GrailsTagLibClass tagLibClass = grailsApplication.getTagLibClasses().find { it.namespace == namespace && it.hasTag(tag)}
def tagLibBean = grailsApplication.mainContext.getBean(tagLibClass.getFullName())
out << tagLibBean."${tag}"(attrs)

Grails Criteria Projection - No signature of method projections() is applicable for argument types

As per Grails documentation
Grails also lets you write your domain model in Java or reuse an existing one that already has Hibernate mapping files. Simply place the mapping files into grails-app/conf/hibernate and either put the Java files in src/java or the classes in the project's lib directory if the domain model is packaged as a JAR. You still need the hibernate.cfg.xml though!
So This is exactley what i did.
I have used java domain model and hibernate.cfg.xml file for mapping. I also use
{DomainName}Constraints.groovy for adding Grails constraints. I also used to add functions to {DomainName}Constraints. For example, below is the content of my EmployeeConstraints.groovy
Employee.metaClass.static.findByDepartment = {depCode ->
createCriteria().list {
department{
inList ('code', depCode)
}
}
}
Now this works fine. But, when i add projection to it(code below), just to get the employee code.
Employee.metaClass.static.findByDepartment = {depCode ->
createCriteria().list {
projections { property('empCode', 'empCode') }
department { inList ('code', depCode) }
}
}
I get the below error..
" No signature of method: com.package.script142113.projections() is applicable for argument types.. "
Can someone point me to whats wrong with the code?
Thanks!
The property projection is used to return a subset of an object's properties. For example, to return just the foo and bar properties use:
projections {
property('foo')
property('bar')
}
You're getting an error because you've called the property method with 2 arguments instead of one.
By the way, I see another potential with your code. Grails will automatically create a dynamic finder findByDepartment that has the same name as the method your trying to add via the meta-class. I have no idea which one will take precendence, but I would suggest you avoid this potential problem and simplify your code, by adding this query using Grails' named query support, and call it something like getByDepartment so that the name doesn't class with a dynamic finder.
The answer by Dónal should be the correct one, but I found a strange behavior with grails 3.1. I got the same message using this call:
Announcement.createCriteria().list {
projections {
property('id')
property('title')
}
} .collect { [id: it['id'], title: it['title']] } // it['id'] not found
I fixed it by removing projections closure:
Announcement.createCriteria().list {
property('id')
property('title')
} .collect { [id: it['id'], title: it['title']] } // got the it['id']
Hope this help.

How can I force Grails to use only one language?

I want to make my Grails application support only one language, that I can define somewhere, completely ignoring the client's headers or the "lang" parameter. Is there any way I can do so? Thanks.
Define a LocaleResolver bean in your config/spring/resources.groovy to set the default locale.
beans = {
localeResolver(org.springframework.web.servlet.i18n.SessionLocaleResolver) {
defaultLocale = new Locale("de","DE")
java.util.Locale.setDefault(defaultLocale)
}
}
This is useful if you don't have to deal with the lang parameter - otherwise it would get overridden. To even ignore the lang parameter value you can set the locale in a Filter upon each request:
import org.springframework.web.servlet.support.RequestContextUtils as RCU
...
def filters = {
all(controller:'*', action:'*') {
before = {
def locale = new Locale("sv","SV")
RCU.getLocaleResolver(request).setLocale(request, response, locale)
}
}
}
This approach seems a bit repetitive as Locale is re-set on every request. It would be more elegant to disable the browsers locale detection via an config option.
The default LocaleResolver of Grails is SessionLocaleResolver. If you want to always use de_DE you can change this to FixedLocaleResolver.
beans {
localeResolver(FixedLocaleResolver) {
locale = new Locale("de", "DE")
}
}
If you want to restrict to a set of locales, then you will need a filter, and use the SessionLocaleResolver#setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) method.
remove all messages_xx.properties files and keep only the messages.properties files.
This is the default message bundle to which the system will always fall back if it can't find the right message bundle.
This way you can still use messages (and thus keep the option to nationalize your app) but users will get always the same language.
This worked for me in order to override the default localResolver bean
beans = {
localeResolver(org.springframework.web.servlet.i18n.FixedLocaleResolver) {
setLocale(Locale.US)
}
}

override grails g:link tag

I am trying to override the g:link tag so that I can prefix an extra string. Here is my code:
import org.codehaus.groovy.grails.plugins.web.taglib.*
class ApplicationTagLib {
static namespace = "g"
def link = { attrs, body ->
if("es".equalsIgnoreCase(request.stLocale.language)) {
attrs['controller'] = "es/" + attrs['controller']
}
def applicationTagLib = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
applicationTagLib.link.call(attrs, body)
}
}
This works fine except for when I add "es/" the resulting path gets translated into es%2F instead of es/ which causes the link to not work.
Is there a way to prevent this from automatically encoding the new slash or a better way to prefix this string to the controller path?
You should be aware that in Grails the controller package (thus it's location in the project's structure path) does not correlate with the default URL mapping - the structure is flattened.
The slash you add to the controller name is thus encoded as it would otherwise form a part of the URL (and thus not map to a controller).
Perhaps the logic for handling different locale be better placed in a controller anyway.
You can add this '/es' prefix in all links generated by grails tags by configuring your UrlMappings.groovy. If you're using the default one, generated by grails create-app command, you can add '/es' in your URL's like this:
class UrlMappings {
static mappings = {
"/es/$controller/$action?/$id?" { // <---------- added '/es' prefix
constraints {
// apply constraints here
}
}
"/"(view: "/index")
"500"(view: '/error')
}
}
To learn more about URL Mappings, see the Grails guide.
Regards

Resources