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
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.
In my Grails application, I have a URL mapping like:
/section/$title/object/$id
IDs are not a number. They are strings which can contain .. Grails is removing everything after the first ., so that if I have /section/books/object/affx.1.2.10, the controller method is only receiving affx.
I am aware that file extension processing can be turned off globally, but that is not what I want. I need to keep the full ID string ONLY for this URL. How can I either do that for this one URL mapping, or add some logic to when Grails will remove file extensions?
Add constraints section to your url-mapping for example:
"/test/$id"(controller: test, action: 'index') {
constraints {
id(matches: /.+/)
}
}
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
}
}
I have some experience maintaining Grails apps; now creating a "task management" application as an exercise.
Apparently there is a view dichotomy of Groovy Server Pages versus Controller actions that render a view, as evidenced by this snippet from a URLMappings.groovy example:
static mappings = {
// ..
"/" (view:'/index')
"/login/$action?" (controller: 'login')
"/logout/$action?" (controller: 'logout')
"500" (view:'/error')
}
where user-facing URLs must be mapped to either views (GSPs) or controllers rendering a view, e.g.:
class LoginController {
/**
* Show the login page.
*/
def auth = {
// .. auth logic
String view = 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl, rememberMeParameter: config.rememberMe.parameter]
}
}
From a design perspective, how do I choose which method to use? When do I create views with GSPs/taglibs like typical server pages outputting HTML, and when do I map a URL to a controller that renders through a delegate GSP? Can I combine both approaches? Have I oversimplified the options here?
To add to what hvgotcodes said, related to your question, the only time you'd want to map directly to a GSP view is when that view is effectively "static".
By static I mean that it isn't relying on the database or any real calculations for rendering the view. It can still be dynamic in that it relies on tag libraries for dealing with common elements, and things like the "Welcome user" text at the top of pages.
As soon as you want to deal with user-supplied input, looking up database information, manage more complicated URLs, or include calculations, you should be using a controller.
The end goal is that GSPs only contain visual and layout information, as well as the occasional static block of text. But you should always avoid mixing any logic in with the GSP, because it clutters the code and always leads to maintenance headaches later on.
Edit regarding Tag Libraries:
As I wrote below:
Tag libraries are for any logic that is connected to the view, like looping over elements, or toggling the visibility of something. Whenever you are tempted to put code directly into your GSP, it probably should be put in a tag library. Of course, there are always exceptions for one-offs.
So, if you have logic code in your view, that specifically relates to visual or layout content, that should be put in a tag library. A good example is the <sec:ifLoggedIn> tag from Spring Security Core, which can be used to toggle the visibility of an element if the user is logged in. This is much better than writing it manually like so:
<sec:ifLoggedIn>blah blah</sec:ifLoggedIn>
<g:if test="${session.user?.loggedIn}">blah blah</g:if>
Because it makes the purpose clearer (by its title), as well as abstracting the logic away, so if you later need to change the way something works, you only have to change it in one place.
tl;dr:
GSPs - Simplified "static" content
Tags - Reusable dynamic components specifically for visual or layout content
Controllers / GSPs - dynamic content
I don't think it's a dichotomy. GSPs and Controller actions (are intended to) work in tandem, the controller invoking services to load data in preparation for passing that data to the appropriate GSP.
The url mapping stuff is for if you want to break the Grails convention for urls, which is orthogonal to how loading data and displaying data (are supposed) to work.
The only time (IMHO) there is a dichotomy is when developers in a project code functionality inconsistently; i.e. it is certainly possible to give the appearance of a dichotomy.
I'm new to Grails and am trying to build a CMS with it.
I want the navigation menu to read from the database so a new page will automatically get a link in the navigation. I've been reading Grails: use controller from index.gsp and related questions, but the answers don't seem to work for me. :(
I've created a domain class named Navigation and a template called _header.
In the "Navigation/list" namespace everything works fine, but outside I can't get to the Navigation data.
I've setup url mapping like so:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(controller : "Navigation", action : "list")
"/"(view:"/index")
"500"(view:'/error')
}
}
But that doesn't seem to work. Any clues on what could be the problem?
You have two mappings for "/", your new one and the original one: "/"(view:"/index") - for starters you'll need to remove the other one.
Not sure if you are aware of this, but there is an open source CMS built in Grails called Weceem. If you need to use it as part of another Grails application, there is also a grails plug in for Weceem, so you can use it as part of your app.
It might worth looking into it before building a complete new CMS :-)
I was looking at the problem all wrong, urlmapping only made the index.gsp redirect to navigation/list. What I was looking for was the
DomainClass.findAll( String query )
propertie to use in the g:each tag
<g:each in="${Navigation.findAll('from Navigation as n where n.css=?', ['ctBoven'])}" var="oNavigation" status="i">
This allows me to read any database from any page.