3

Anybody who already have implemented something similar using Grails could tell me please which are the good pratices (if there are any) to create user profile URLs with the format "http://www.myservice.com/username", as in Facebook, Twitter, Linkedin?

I'm trying to implement it through the UrlMappings and appears to me I'll need to break with the code conventions, at least for the Controllers.

So, any suggestions are welcome, thanks.

UPDATE 1

When I mentioned my concern about breaking the code conventions, what I'm saying is that I want to show the user profile using this mapping, but I do have other objects in my application which I would like to access using the default mapping:

"/$controller/$action?/$id?"()

SOLUTION

Thanks to the great contributions I've received here, I've camed up with this solution, which solves my problem.

As was pointed out, to do this kind of mapping, I'll need to control more closely how my requests are handled. That means I'll need to tell to Grails which controllers I don't want to be mapped to the "username" rule.

Since that will be a very tedious task (because I have several controllers), I did this to automate it:

UrlMappings.groovy

static mappings = {
     getGrailsApplication().controllerClasses.each{ controllerClass ->
     "/${controllerClass.logicalPropertyName}/$action?/$id?"(controller: controllerClass.logicalPropertyName)
     }
     "/$username/$action?"(controller: "user", action: "profile")
     }
     ...
 }

And of course, I'll need to do something similar in my user registration process to avoid usernames to be equal to some controller name.

That's it, thank you all.

Uilian
  • 656
  • 8
  • 19
  • [this tutorial](http://www.ibm.com/developerworks/library/j-grails03109/) does something very similar, I guess you can get some directions from it. (Section `Displaying entries by author`) – moeTi Apr 10 '13 at 13:31
  • Thanks, but that isn't quite what I need. This tutorial deals with the mapping problem, but avoids the multiple possible matches to an URL by preffixing it with "/blog/". – Uilian Apr 10 '13 at 16:05

2 Answers2

4

Assuming you have a UserController and you are going to map any domain.com/username to the show action of user controller, your url mapping could be something like this : In my example, name will become a parameter in your params. for further details refer to here

Hope this helps.

static mappings = {
    "/$name"(controller: "user", action: "show")

    ...
}
Alidad
  • 5,463
  • 1
  • 24
  • 47
  • Thanks! Your assumptions are all correct, this is exactly what I'm doing. But this is causing some undesired collateral effects. The most annoying is that before applying this mapping, I could obtain de default action of a controller just using http://example.com/controllername. This is why I'm asking if there are a better way, or if this is the only method and I have to deal with it. – Uilian Apr 10 '13 at 16:01
  • If I understand you correctly, you do not want to use any controller name in your url. if that is the case "/$name"(controller: "user", action: "show") does not need any url, it will rout www.domain.com/Uilian to user controller any action that is assigned to handle your request, in my example "show" – Alidad Apr 10 '13 at 16:10
  • You're right @alidad, I want to show the user profile using this mapping proposed by you, but I do have other objects in my application which I would like to access using the default mapping. I'll update my question with this information, thanks. – Uilian Apr 10 '13 at 16:23
  • I see, you can have other mapping there as well, you can keep the conventional controller/action one there and also add your own. Grails will try to match the pattern so you need to make sure you are not overlapping with your patterns. – Alidad Apr 10 '13 at 16:30
1

Given your requirements, everything after http://yourdomain.com/ can be a username or one of your other controllers, which can have undesired effects depending on which url mapping is defined first (e.g. user controller vs nonuser controller). But most likely the nonuser controller list will be the smaller list, so you should place that first and filter against it, then treat all other url mappings as user mappings.

Here is an example:

static mapping = {
  "/$controller/$action?/$id?" {
    constraints {
      controller inList: ['nonUserController1', 'nonUserController2',...]
    }
  }

  //this should work for /username and /username/whateveraction
  "/$username/$action?"(controller: 'user')
}

Some things to note here:

  • you need to place the non user controller url mapping first, since everything else after http://yourdomain.com/ may be a username - correct?
  • second you need to place a constraint to catch all the non user controllers, while ignore the user url mappings
  • also you need to prevent a user from signing up with a username that matches one of your non user controllers
ikumen
  • 11,275
  • 4
  • 41
  • 41
  • Yes, I think you're right. I'm just not sure if by the UrlMappings precedence rules the second one (aimed to match usernames) will not ending be the first one to be evaluated, since it has less wildcards. But I really liked this approach, its much cleaner than what I was doing, thanks! – Uilian Apr 11 '13 at 00:53
  • Ok, I've tested you solution, and as I suspected, all the requests are being mapped to the "/username/$action?", due the precedence rules. But your suggestion was indeed very helpful, and I came up with a solution, I'll update my question with it. Thank you! – Uilian Apr 11 '13 at 14:13