Grails webflow url rewriting - grails

Want to make the URLs SEO friendly when using grails webflow. It is quite limiting with the convention grails uses and hard to go around the way it's built.
For example, i have a flow called fooProcess in the controller called FooController, when i trigger the flow i would like the display: /foo/bar/test, instead of /foo/fooProcess?excecution=e1s2
class FooController {
def fooProcessFlow {
showFoo {
}
}
}
I tried using redirect and specify the uri but that's not supported, grails complains that the page isn't found
fooProcessFlow {
showFoo {
redirect(uri:"/foo/bar/test")
}
}
grails/foo/fooProcess.dispatch/externalRedirect:/foo/bar/test
Also, a redirect is an end state in a flow, if I only want to render the page, i have to use the render method and specify the view name or structure my views according to webflow convention.
fooProcessFlow {
showFoo {
render(view:"/foo/bar/test")
on "add".to "add"
}
}
The url will be in this case
/foo/fooProcessProcess?execution=e6s1
Anyone dealt with this case before ?
Did anyone use UrlRweriteFilter with webflows in grails
http://code.google.com/p/urlrewritefilter/
ken

You can use URLMappings Grails Plugin
See: http://grails.org/doc/latest/ref/Plug-ins/URL%20mappings.html
Edit this file: grails-app/conf/UrlMappings.groovy
Putting something like this inside:
class UrlMappings {
static mappings = {
"/foo/bar/test" (controller: "foo", action: "fooProcessFlow")
"/$controller/$action?/$ids?"{
constraints {
}
}
}
}

Related

Override Grails redirect method

I'm carrying the locale of my Grails application in the URL, such as
http://myapp.com/LANG/controller/action/id.
Therefore, I adjusted the URLMappings.groovy with the language attribute. Since I would like to save work and don't apply the lang parameter at every link, I have overridden the g:link taglib with the language from params.lang. This works pretty good.
When I do a redirect from a controller, such as
redirect(action: "logout")
I'd like to append the params.lang automatically instead of writing every time
redirect(action: "logout", params: [lang: params.lang])
I found the thread grails override redirect controller method, but I'm unsure how to proceed. Where does this code take part?
How can I override the redirect() method and add the params.lang-attribute?
Please advice, thank you very much.
The easiest way to do this in your application would be to use a bit of meta programming and the BootStrap.groovy for your Grails application. The following is just a simple example of what that might look like:
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
class BootStrap {
def init = { servletContext ->
def ctx = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
def app = ctx.getBean("grailsApplication")
app.controllerClasses.each() { controllerClass ->
def oldRedirect = controllerClass.metaClass.pickMethod("redirect", [Map] as Class[])
controllerClass.metaClass.redirect = { Map args ->
// new pre-redirect logic
if (!args['params']) args['params'] = [:] // just in case redirect was called without any parameters
args['params']['whatever'] = 'something' // add something into the parameters map
oldRedirect.invoke delegate, args
// new post-redirect logic
}
}
}
def destroy = {
}
}
The above example just wraps the implementation for redirect on all controllers within your Grails application and injects a new parameter called whatever with the value of something.
The above was tested (quickly) using Grails 2.4.2.

Learning Grails, Definitive guide to Grails, 2nd ed. Stuck on Urlmapping in chapter 4

I'm learning Grails from the Definitive Guide (2nd ed), and get stuck on the Urlmapping used.
I'm buidling the gTunes store, as many before me must have done.
The book makes me change the default mapping in:
class UrlMappings {
static mappings = {
"/"(controller:"store")
}
}
On the form rendered by the "store"'s default action, there is this code:
<div id="registerPane">
Need an account?
<g:link controller="user" action="register">Signup now</g:link>
to start your own personal Music collection!
</div>
clicking, the server returns the message "The requested resource (/gTunes/user/register) is not available."
I have a register.gsp in NetBeansProjects\gTunes\grails-app\views\user
If I change the UrlMappings to it's original, click on StoreController in the original Homepage, and then click SignUp it works.
So the problem lies in the mapping used as specified in the book. I found the ERRATA for the first but not for the second edition.
Sorry, missed a ? after action:
class UrlMappings {
static mappings = {
"/"(controller:"store")
"/$controller/$action?/$id?"{
constraints{
}
}
}
}
Your URLMappings seems incomplete. it should be:
class UrlMappings {
static mappings = {
"/"(controller:"store")
"/$controller/$action/$id?{
constraints{
}
}
}
}
This is not in the book, but the solution here fixes the problem. The book apparently uses 1.1 version of grails.

dependency inject Grails Webflow

Is it possible to inject a Spring bean into a Grails webflow? I tried the following
class CheckoutController {
ShoppingService shoppingService
def checkoutFlow = {
start {
action {
// This attempt to access the service doesn't work
flow.addresses = shoppingService.getOrder()
}
}
}
}
I can access shoppingService from a regular controller action, but I can't access it from an action of the webflow (see above).
add the following to your controller:
def transient shoppingService
There are issues with dependency injection with webflows in controllers that contain traditional actions plus webflows. It worked for me if the traditional action executed first.
see:
GRAILS-7095
GRAILS-4141
Webflows also break notions of defaultAction in mixed controllers. I have found the first webflow wins and becomes the default action.
separately using transient keeps your service from be serialized between flow states. (e.g. don't have to implement serializable)
At first I thought what you listed was pseudocode but I made a sample app using your example and got the NPE as well. I think it may be your flow structure that is the problem. action blocks should go within a flow state. Your flow definition should look something like:
class CheckoutController {
ShoppingService shoppingService
def checkoutFlow = {
start {
action {
flow.addresses = shoppingService.getOrder()
if(flow.addresses) {
showForm()
}
else {
showError()
}
}
on("showForm").to "showForm"
on("showError").to "showError"
}
showError {
...
}
//etc.
}
}
You can definitely use injected services in your web flows. I am guessing that the problem lies in your flow structure.

How to dynamically generate urls used by grails.plugins.springsecurity?

This is not one of the simple questions.. So let me explain it in detail...
The background:
In my grails application I have this mapping:
class UrlMappings {
static mappings = {
"/$storeId/$controller/$action?/$id?"
...
}
}
This means that all url's my application will process has preceding url parameter "storeId". All controllers will use this parameter to render content and perform other actions...
Also I have controller with annotation
#Secured(['IS_AUTHENTICATED_REMEMBERED'])
class SomeController {
def index = {
// ...
}
}
Let's say user is trying to access this page:
/555/some/index
If this is unauthenticated user, security-plugin will redirect him to the
/login/auth
The issue is:
When spring-security will redirect to this page, user will see 404. This happens because login controller will handle only urls like
/555/login/auth
The question is:
What should I do to dynamically build this url?
p.s.
It turns out that:
There is config param for
spring-security, called
"auth.loginFormUrl". But this is just
static text. And I need to build this
url based on what url influenced this
redirection
The redirection is done (I'm not 100%
sure) in org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint#commence.
But how to override it?
Typically that's a static value, but it's called from LoginUrlAuthenticationEntryPoint.getLoginFormUrl(), so you could subclass that and do a dynamic calculation of the url in an overridden method. The plugin already subclasses it with org.codehaus.groovy.grails.plugins.springsecurity.AjaxAwareAuthenticationEntryPoint so you should extend that.
To register your implementation instead of the plugins, add a bean override in grails-app/conf/spring/resources.groovy:
import com.yourcompany.yourapp.YourAuthenticationEntryPoint
beans = {
authenticationEntryPoint(YourAuthenticationEntryPoint) {
loginFormUrl = '/login/auth' // has to be specified even though it's ignored
ajaxLoginFormUrl = '/login/authAjax'
portMapper = ref('portMapper')
portResolver = ref('portResolver')
}
}
You don't have direct access to the request in this method, but it's available in a thread-local - call org.codehaus.groovy.grails.plugins.springsecurity.SecurityRequestHolder.getRequest().

How to prevent direct access to *.gsp pages in Grails

Could anyone advise a feasible solution to prevent direct access to *.gsp pages on Grails?
After reviewing intercepting '/**.gsp', I found it is impossible to use that as it not only filters out direct access, but also the pages rendering from controllers, etc.
I tried to setup the following in UrlMapping.groovy, even though I can prevent the *.gsp direct access, but I also make a mess to the navigation of the pages; all the links seem to go to home page then.
"/**.gsp" {
isEligible = {
System.err.println("ALL PARAMS: " + params)
request.requestURL.toString().endsWith(".gsp")
}
controller = {
if (request.requestURL.toString().endsWith(".gsp")) {
"public"
} else {
"*"
}
}
action = {
if (request.requestURL.toString().endsWith(".gsp")) {
"home"
} else {
"*"
}
}
}
Once I thought about setup filter like org.springframework.web.filter.OncePerRequestFilter, but not quite sure how to define it probably as Grails tends to generate the web.xml filters section by itself.
Any thoughts?
Thanks a lot!
tom
unfortunately I did not find a solution with UrlMappings.
here is a solution which is little bit ugly but if you use the same layout (for example main.gsp) on all pages you could add this lines to the layout (main.gsp).
<% if (request.requestURL.toString().endsWith(".gsp")) {
response.sendRedirect("${request.contextPath}/")
} %>
this way if the user tries to access the gsp page direct he gets redirected to the home page.
maybe not the best solution but did work for me so far.
cheers shifty
Add these to UrlMappings:
"/**.gsp" {
controller = {
if(request.requestURL.toString().endsWith(".gsp")) {
"forbidden"
} else params.controller
}
}
And create a ForbiddenController and an index.gsp with "Never think of accessing GSPs directly dude." as its content.
Cheers.
according to the grails FAQ the "/**.gsp" configuration in the UrlMapping.groovy should work.
couldn't try it out yet.
How did you add the links to the page ?
Are the links also broken when you use the link tag ?
<g:link controller="book" action="list">Book List</g:link>
What about writing a filter that will be executed on each request ?

Resources