I have a weird error going on where when I start my Grails application and see the default home page, no matter what controller I click on I get a 404 not found. But if I change my URL mappings file to point a particular controller and method, then the associated view is rendered fine. I tried typing the url as 'controller/method' but that also gave the 404 and I know that the methods aren't being executed because my test log statements don't print. The only way my views are coming up is if I force them using the URL mappings file and then the test log statements in the methods will also print.
Make sure you have a URL mapping defined which supports the controller/action pattern.
class UrlMappings {
static mappings = {
// you probably want a mapping like this...
"/$controller/$action?/$id?(.$format)?"{
}
// other mappings here
}
}
Related
Currently using grails 4.0.3.
I'm trying to generate a link to a controller action using a simple <g:link> tag:
<g:link controller="myController" action="myAction" params="[someParam:'myValue']">Link text</g:link>
In my UrlMappings file, I have this controller mapped to a common URL for external calls. This mapping forces some parameters that I want forced when coming to this mapping:
class UrlMappings {
static mappings = {
"/service/someExposedUrl" {
controller = "myController"
action = "myAction"
someParam = "defaultValue"
}
}
}
However, the link that gets written to the page in my application gets written using the UrlMapping definition.
http://serverUrl/service/someExposedUrl?someParam=<ignored>
instead of
http://serverUrl/myController/myAction?someParam=myValue
In this case, the parameters are ignored on the /service URL because they're hard-coded in the UrlMapping.
Is there a way to force grails to link to the specified controller/action instead of the mapping?
Up front caveat: I have not tested this for this specific situation, though I have used these pieces individually. Second caveat: I would not be remotely surprised to learn there's an even better way to do this...grails has a lot of options sometimes!
You should be able to create a named mapping in your UrlMappings, then reference that in your createLink.
UrlMappings:
name arbitraryName: "/myController/myAction" {
controller = "myController"
action = "myAction"
}
Link:
<g:link mapping="arbitraryName" params="[someParam:'myValue']">Link text</g:link>
You may be able to get what you need with a combination of Custom URL mapping and the exclude keyword in the original URLMappings.
Create MyControllerUrlMappings. This guide gives several examples of different ways of writing them out. I found it helpful.
class MyControllerUrlMappings {
static mappings = {
// Url Mappings specific only to this controller
}
}
https://guides.grails.org/grails_url_mappings/guide/index.html#_multiple_urlmappings_files
Your app will still be running the original UrlMappings file though. So you'll have the new custom Url mappings and the default ones. To fix that you can exclude your controller and it's methods in the original UrlMappings file.
class UrlMappings {
static excludes = [
"/myController",
"/myController/*"
]
static mappings = {...}
...
Not as elegant as I'd like, but in a similar situation this is how I got it working.
Grails always provides URL for every method in a Controller. For example, if there's a method branch() in controller repository, there would be automatically a URL for localhost:8080/project/repository/branch.
What I want is to disable this. So unless I declare that URL to URLMappings.groovy, accessing this webpage will render a 404 Error, even this Controller/method exists.
I'm using Grails 2.4.4 by the way.
You should remove this code, from your UrlMappings:
"/$controller/$action?/$id?(.$format)?" {
constraints {
// apply constraints here
}
}
Now you will get 404 if you haven't declared mapping for each action.
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 trying to create a mock rest service. There are basically two components to this. I need a controller with actions and views that allows me to create ResourceMappings where I create a mapping between a uri and a mock response that I will pass back. The second component is a catch-all Grails url mapping for every other url so that when they hit the uri of this application they will be sent to my catch all controller that will return the mapped response that they created earlier.
For example... I go to the url http://someserver.com:1234/restMapping/list. This url is the exception to the catch all rule. It takes me to some views that allow me to create a rest uri mapping. Here I create the mapping /mockservice/test and give it the response "This is a test" with a content type of text/plain. Now, if I go to the url http://someserver.com:1234/mockservice/test I should hit the catch all that sends me to a controller that returns a page with the content type text/plain and the response "This is a test".
I have tried the following and it does not seem to work. Does anyone have any ideas?
static mappings = {
"/$control/**" {
controller = "catchAllHandler"
action = "index"
constraints {
control(validator: {!['restMapping','css','js','images'].contains(it)})
}
}
"/$controller/$action?/$id?"{
}
"/"(controller:"restMapping", action="index")
"500"(view:'/error')
}
An interesting thing to note is that when I get rid of ** and add in tons of extra variables like $s1?/$s2?/$s3? etc then it does seem to work. The problem is that I don't know how long the uri is that I am trying to map so I would rather use the ** to catch everything exception the few exceptions that I have.
I finally figured it out. I needed to include WEB-INF in my list to exclude. I now use the static excludes field as well as the validator to exclude specific controller urls.
class UrlMappings {
static excludes = ["/images/*","/css/*","/js/*","/WEB-INF/*"]
static mappings = {
"/restResourceMapping/$action?/$id?"{
controller = "restMapping"
}
"/$control/?**" {
controller = "catchAllHandler"
action = "index"
constraints {
control(validator: {!['restMapping'].contains(it)})
}
}
"/"(controller:"restMapping", action="index")
"500"(view:'/error')
}
}
I decided to just exclude all url mappings that should never be any of my rest urls. These included /images, /css, /js, and /WEB-INF. I can now create urls of any length and have them go to my catch all controller. If the person goes to the base url or the restMapping url set then they will be taken to the crud pages where they can create new rest resource mappings. If I want to create any other controllers and views that I want to go around the catch all controller I can simply add it to my validator and make them process normally.
Also you might notice that I am using a ? in the catch all right after the /. This seems to make it so my catch all works with urls that only have one word after the server name as in http://server.com:1234/something.
I am not sure about this, but I think the order in which the URL mappings are defined is important. So try listing the URL mapping for your special cases at the beginning of the mappings closure and then list the general ones (the one that uses **). Do let me know if this works :)
I'm developing a Grails web application (mainly as a learning exercise). I have previously written some standard Grails apps, but in this case I wanted to try creating a controller that would intercept all requests (including static html) of the form:
test 1
test 2
test 3
test 4
The intent is to do some simple business logic (auditing) each time a user clicks a link. I know I could do this using a Filter (or a range of other methods), however I thought this should work too and wanted to do this using a Grails framework.
I set up the Grail UrlMappings.groovy file to map all URLs of that form (/$myPathParam?) to a single controller:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
}
}
"/$path?" (controller: 'auditRecord', action: 'showPage')
"500"(view:'/error')
}
}
In that controller (in the appropriate "showPage" action) I've been printing out the path information, for example:
def showPage = {
println "params.path = " + params.path
...
render(view: resultingView)
}
The results of the println in the showPage action for each of my four links are
testJsp.jsp
testGsp.gsp
testHtm.htm
testHtml
Why is the last one "testHtml", not "testHtml.html"?
In a previous (Stack Overflow query) Olexandr encountered this issue and was advised to simply concatenate the value of request.format - which, indeed, does return "html". However request.format also returns "html" for all four links.
I'm interested in gaining an understanding of what Grails is doing and why. Is there some way to configure Grails so the params.path variable in the controller shows "testHtml.html" rather than stripping off the "html" extension? It doesn't seem to remove the extension for any other file type (including .htm). Is there a good reason it's doing this? I know that it is a bit unusual to use a controller for static html, but still would like to understand what's going on.
This is related to content negotiation, which you can read about in section 6.8 of the Grails user guide. If Grails recognises the extension as a particular type, the extension is removed from the URL and the type is added to the "format" parameter.
You can disable this behaviour by adding this entry to grails-app/conf/Config.groovy:
grails.mime.file.extensions = false