I'm kind of new to grails and I'm trying to just map a basic URL request to a view.
So, say I have a view, /x/index.gsp and I want the user to be able to go to it. There will also be /y/index.gsp, /z/index.gsp, etc.
I defined it like so:
"/$customer/index" { view = {params.customer+"/index"} }
This seems to throw an exception though. I also have :
"/$customer/$controller/$action?/$id?" { }
which does work and I don't want to have to create a controller that doesn't really do anything but handle the index call and show it.
I'm sure I'm missing something simple but I don't know what it is.
The reason the first mapping fails is because it can't figure out what controller to route the request to.
To fix it, you need to define what controller you want the top mapping to route to. This is how I did this in a recent project of mine:
"/uploaders/$id" {
controller: "uploader"
}
To map to just a view:
"/$customer/index"(view: "/${params.customer}/index")
Related
I have a Controller structure that looks like this:
[Controllers]
[Users]
[PlayersController.cs]
[ServicesController.cs]
Where Players and Services are the two different types of users.
If i call the controller like this: localhost:xxxx/Users/Players/ index is being called, as expected, but my View is not rendered. My view is under ~/Views/Users/Players/Index.cshtml but ASP.NET is not looking for it under that folder.
I read that I should use EnableCodeRouting but I'm not sure where and how to use it:
https://github.com/maxtoroq/MvcCodeRouting/blob/master/docs/api/MvcCodeRouting/CodeRoutingExtensions/EnableCodeRouting_1.md
I added a that method under App_Start\RouteConfig.cs but it's never being called.
How do I, without typing in the View link in the controller, tell MvcCodeRouting where my View is located?
I.e. I want to use return View(vm) not return View("~/Views/Users/Players/index.cshtml").
In global.asax.cs.Application_Start() add this: RegisterViewEngines(ViewEngines.Engines);
And in the same class create this method:
void RegisterViewEngines(ViewEngineCollection viewEngines)
{
// Call AFTER you are done making changes to viewEngines
viewEngines.EnableCodeRouting();
}
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.
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")
...
}
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
Somehow I have gotten sideways with my Grails MVC mapping and I do not understand how.
I have a controller, AController, which I generated using the Grails command line wizard. At a later date I generated a view for that controller to customize the view.
AController is in [project]/grails-app/controllers/[package]/AController.groovy and the view .gsp's are in [project]/grails-app/views/A/.
The URLMappings.groovy has:
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
When I run the application and enter a url of the form: localhost:8080/[project]/A/list I reach, as expected, the method A.list in AController.groovy.
However, when I then return from A.list expecting the framework to route to list.gsp in [project]grails-app/views/A/ I see a 500 error with the message:
"URL mapping must either provide a controller or view name to map to!"
Obviously I am doing something stupid but I cannot see what it was that I broke. URLMappings.groovy looks correct. File locations look correct. Scaffolding seems properly customized.
Any suggestions?
Du-Oh
The problem was that there was no .count for an array. For some reason my brain insists on .count and not .size(). Stupid human error.
I am wondering if you have tried to make a mobile version of a controller?
Right now I am extending GrailsLayoutDecoratorMapper with my custom MobileDecoratorMapper which applies layout.mobile.gsp if the mobile phone is detected and I would like to do something similar for some controllers. My idea is to check in the filter if there existing a mobile version of a controller (for example SomethingControllerMobile or SomethingController.mobile.groovy) and if so redirect to it instead of a default SomethingController.
The reason for that is I would like to avoid a lot of if/else statements inside controller itself to check whether it is mobile, and if so do something differently - i do not want spaghetti code.
Does it makes sense to you and if so have you tried to do something similar and what was your approach? The only thing that comes to my mind is check for files in the filter but it does not look like a proper solution, i think this should be possible to be done on urlmapping level, where on the basis of the url grails decides which controller to invoke
The Spring Mobile plugin allows you to conditionally execute controller code for mobile devices in a fairly elegant fashion
def list = {
def view = "list"
withMobileDevice {
// mobile-specific logic goes here, in this simplistic example we
// just change the view, but you can do anything you like....
view = "mobileList"
}
render(view: view, model: [list: listInstance])
}
I am attempting to construct an asp.net mvc app which will use the urls like:
/Controller/[Number]/Action/Id
I have got it to always call my controller and pass it the Number and the Id fine...
However I now want to return a different view depending on the Number
I could have options like:
if([Number] == 1) { return View("ViewName");}
if([Number] == 2) { return View("ViewName2");}
however I instead was wondering if there was a way to change the core so that instead of searching at ~/Views/controller/action.aspx I could have my own method which did some checking on the Number then passed to the virtual file provider is a different path
Hope this makes sense!
Decide which view to load, depending on input parameters is a controller task. You could write your own view engine.
But it is easier to return the full path to the view you want to return.
return View("~/myviews/ViewName3.aspx");
This will render ViewName3 from given directory.
You might want to look at decorating your controller method with Action Filter Attributes.
Then, you could do something special inside the Action Filter Attribute.
Or, you could pass Number to a Model object, then have the model Object return the right View path.
Either way, your instinct of trying to keep too much logic out of the Controller is sound, especially if [Number] is somehow a business concern and not a view concern.
You need to look into / google creating a custom view engine.
By the sounds of things you probably just want to extend the built-in WebFormViewEngine and just override the locations and the .FindView() method.
HTHs,
Charles