I use the following url mapping in Grails:
"/$id"{
controller = "user"
action = "show"
}
to map urls like mydomain.com/someusername
How do I attach constrains to the url mapping to exclude keywords like "login", "logout",...
I.e.,
mydomain.com/someusername should route to mydomain.com/user/show/someusername,
mydomain.com/login should not route to mydomain.com/user/show/login.
You can use contrainsts for this mapping:
"/$id"{
controller = "user"
action = "show"
constraints {
//add a validator for $id from url mapping
id(validator: {
return !(it in ['login', 'logout'])
})
}
}
Use a filter, and redirect from it.
class UrlMappingFilters {
def filters = {
filterUrlKeywords(controller: '*', action: '*') {
def keywords = ['login', 'logout']
before = {
if (params.id in keywords) {
// redirect to start, or render error...
redirect(uri: '/')
return false
}
}
}
}
}
If you want to make it specific to a controller or action, use its name instead of the '*'.
Read more:
Official docs on Grails filters
Reference
I have done this type of thing for REST based endpoints numerous times, and Grails is smart enough to figure out what you want. Note that ordering in the UrlMappings file may be important.
For example, you can define this:
class UrlMappings {
static mappings = {
"/login" { controller: 'auth', action: 'login' }
"/logout" { controller: 'auth', action: 'logout' }
"/$id" { controller: 'user', action: 'view' }
}
}
Then when you hit "/login" you will go in the auth:login method, but when you hit "/userid" you will be sent to user:view:userid as expected.
Related
I organized my url mappings in grails with groups.
Example from UrlMappings:
class UrlMappings {
static mappings = {
group "/rest", {
"/" {
controller = "rest"
action = "index"
}
"/method1" {
controller = "rest"
action = "method1"
}
}
"/webservice/" {
controller = "webservice"
action = "index"
}
}
What I want: When the Url is called /rest/notexists, I want a 404 for this path. But only for /rest group, so the 404 should belong to the group rest. Other 404 are handled by my backbone router.
Any ideas?
I solved it with a catch all at the end of the rest group:
"/**" {
controller = "rest"
action ="notFound"
}
I have a Grails 2.1.1 application which runs fine, at least until I try to define a filter for all Controller to redirect to the index.gsp if user is not set in the session variable...
what ever I try, I'm not able to redirect to "/index", nor render "/index" when starting the server - if I remove the filter and redirect to "/index" from my AuthenticationController on false parameters, all works like charm...
so, here's what I have so far:
class AuthenticationFilters {
all(controller:'*', action'*') {
before = {
def user = (User) session.getValue("user")
if(user == null || !user.loginState) {
redirect(controller: 'authentication', action: 'index')
return false
}
}
}
}
class AuthenticationController {
def authenticationService
def index = {
render(view: '/index')
}
def login(LoginCommand cmd) {
if(cmd.hasErrors()) {
redirect(action: index)
return
}
}
....
}
now, if I comment out the all Filters definition, everything works well. I got the page (index.gsp) shown on start up and if the LoginCommand has errors, I'm redirected to the index.gsp page without any problems.
if I now comment in the all Filters definition, I get a 404.
I tried:
Grails: Redirect to index.gsp that is not in any controller
Upgrade to Grails 2.0: /index.gsp not found
Grails: what are the main issues (and his solutions) when deploying in weblogic?
but I didn't had any luck...
I'm developing on Intellij IDEA 11.1.4 Premium (evaluation)
EDIT: I tried to get the User object from the session property in my AuthenticationFilters class, surrounded by a try/catch block and now facing the problem that obviously the session property is not available? why?
try {
def user = (User) session.getValue("user")
if((user == null || !user.loginState)) {
...
}
} catch(Exception e) {
println("... couldn't get user from session! ${e.getMessage()}")
}
console output:
... couldn't get user from session! No such property: session for class: grailstest001.AuthenticationFilters
any suggestions on this?
So, just to add my experience here to close this question as solved and for further usage for other users:
to check if the user is entering the page for the first time, you could easily do this by checking for the controllerName field. It should be null or "" (empty String) if the user was not referred to the site by any controller.
Also, I'm not using any Database within my application for authentication because all of this issuses are backended by an API. So I created a UserService.groovy class which is acting as a SessionScopedBean and I store all my user related infos within this class.
So, your filter definition could look like:
class MyFilters {
def filters = {
before = {
if(!controllerName || controllerName.equals("")) {
redirect(controller:'home',action:'index')
}
if(!applicationContext.userService?.getUser() || !applicationContext.userService?.getUser.isLoggedIn) {
redirect(controller:'auth',action:'login')
}
}
}
}
Otherwise, if you don't want to redirect the user which 'freshly' entered your page from within your Filters.groovy class, you could use the UrlMappings.groovy class to do so. Just map your / (root) index to the page you want:
class UrlMappings {
static mappings = {
"/"(controller:'mycontroller',action:'myaction') // change it to your liking
"/$controller/$action?/$id?" {
constraints {
// apply constraints here
}
}
"500"(view:'/error')
}
}
I think your filter syntax may be incorrect - try
Class AuthenticationFilters {
def filters = { // <--- added this
all(controller:'*', action'*') {
before = {
def user = (User) session.getValue("user")
if(user == null || !user.loginState) {
redirect(controller: 'authentication', action: 'index')
return false
}
}
}
} // <-- added
}
I am working with Grails 2.1.1 and would like to add a handful of customized URLs that map to Controller Actions.
I can do that, but the original mapping still works.
For example, I created a mapping add-property-to-directory in my UrlMappings as follows:
class UrlMappings {
static mappings = {
"/add-property-to-directory"(controller: "property", action: "create")
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Now, I can hit /mysite/add-property-to-directory and it will execute PropertyController.create, as I would expect.
However, I can still hit /mysite/property/create, and it will execute the same PropertyController.create method.
In the spirit of DRY, I would like to do a 301 Redirect from /mysite/property/create to /mysite/add-property-to-directory.
I could not find a way to do this in UrlMappings.groovy. Does anyone know of a way I can accomplish this in Grails?
Thank you very much!
UPDATE
Here is the solution that I implemented, based on Tom's answer:
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/add-property-to-directory"(controller: "property", action: "create")
"/property/create" {
controller = "redirect"
destination = "/add-property-to-directory"
}
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
RedirectController.groovy
class RedirectController {
def index() {
redirect(url: params.destination, permanent: true)
}
}
It is possible to achieve this:
"/$controller/$action?/$id?" (
controller: 'myRedirectControlller', action: 'myRedirectAction', params:[ controller: $controller, action: $action, id: $id ]
)
"/user/list" ( controller:'user', action:'list' )
and in the action you get the values normallny in params:
log.trace 'myRedirectController.myRedirectAction: ' + params.controller + ', ' + params.action + ', ' + params.id
As of Grails 2.3, it is possible to do redirects directly in the UrlMappings, without the need for a redirect controller. So in case you ever upgrade, you can redirect in UrlMappings like so, as per the documentation:
"/property/create"(redirect: '/add-property-to-directory')
Request parameters that were part of the original request will be included in the redirect.
In recent Grails version (currently 5.x) you need to pass a Map into the redirect: property:
"/viewBooks"(redirect: [uri: '/add-property-to-directory'])
https://docs.grails.org/latest/guide/theWebLayer.html#redirectMappings
I'm using Grails 1.3.6. I have this file ...
grails-app/views/home/design/index.gsp
Here is what is defined in my HomeController. Sadly, whenever I visit, "http://localhost:port/context-path/design/", I get a 404 error. The server starts normally and there are no errors in the logs. What can I do to get my page instead of the 404?
def index = {
def folder = params.folder;
def page = params.page;
if (page) {
try {
def contents = IOService.getFileContents(folder, page)
response.setContentType("application/json")
response << contents
} catch (FileNotFoundException e) {
response.status = 404;
} // try
} else {
render(view: "/home/${folder}/index")
} // if
}
My URLMappings file consists of ...
static mappings = {
"/$folder?/$page"{
controller = "home"
action = "index"
}
"/"(view:"/index")
"500"(view:'/error')
}
Thanks, - Dave
If you want to be able to access
/context-path/home/design
Your action needs to be named design, i.e.
class HomeController {
def design = {
}
}
The Grails convention is always /context-path/controllerName/actionName (unless you have it mapped differently in grails-app/conf/URLMappings.groovy).
Your example's a bit unclear which path you're trying to access. To address both:
If you want /context-path/design, you need a DesignController with an index action (because if no action is supplied in the URL, Grails looks for the index action).
If you want /context-path/home/design, you need a HomeController with a design action.
Edit:
In the comments, you express the want to be able to have /context-path/design map to the HomeController index action. You can do this with grails-app/conf/URLMappings.groovy:
"/design"(controller: 'home', action: 'index')
Since it seems you have two distinct actions, I would set things up a bit differently:
def indexWithPage = {
def folder = params.folder;
def page = params.page;
try {
def contents = IOService.getFileContents(folder, page)
response.setContentType("application/json")
response << contents
} catch (FileNotFoundException e) {
e.printStackTrace();
response.status = 404;
} // try
}
def index
def folder = params.folder;
render(view: "/home/${folder}/index")
}
with a URLMaping of:
static mappings = {
"/$folder/$page"{
controller = "home"
action = "indexWithPage"
}
"/$folder"{
controller = "home"
action = "index"
}
"/"(view:"/index")
"500"(view:'/error')
}
I also threw a e.printStackTrace(); in there to help us determine whether you're getting YOUR 404 or the action is truely not being called.
What is the right way to populate the model for the index page in a grails app? There is no IndexController by default, is there some other mechanism for getting lists of this and that into the model?
I won't claim that this is the right way, but it is one way to start things off. It doesn't take much to have a controller be the default. Add a mapping to UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"500"(view:'/error')
"/"
{
controller = "quote"
}
}
}
Then add an index action to the now default controller:
class QuoteController {
def index = {
...
}
}
If what you want to load is already part of another action simply redirect:
def index = {
redirect(action: random)
}
Or to really get some reuse going, put the logic in a service:
class QuoteController {
def quoteService
def index = {
redirect(action: random)
}
def random = {
def randomQuote = quoteService.getRandomQuote()
[ quote : randomQuote ]
}
}
I couldn't get Ed T's example above to work. Perhaps Grails has changed since then?
After some experimentation and some rummaging on the net, I ended up with this in UrlMappings.groovy:
"/"(controller: 'home', action: 'index')
My HomeController looks like this:
class HomeController {
def index = {
def quotes = = latest(Quote.list(), 5)
["quotes": quotes, "totalQuotes": Quote.count()]
}
}
And in views/home, I have an index.gsp file. That makes the index.gsp file in views unnecessary, so I removed it.
The good answer: If you need to populate a model for the index page, it's time to change from using a straight index.gsp to an index controller.
The evil answer: If you create a filter whose controller is '*', it'll get executed even for static pages.
In grails 1.3.6 for just adding
"/index.gsp"(uri:"/")
to UrlMappings.groovy worked fine for me. It has the same effect as adding a new controller and mappings like described before.
Below is my complete UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"/index.gsp"(uri:"/")
}
}