I have written an grails application, In UrlMappings.groovy file I have url mapping as
"500"(controller:"exception" , action:"internalServerError")
, so whenever any exception occurred I getting the exception in the ExceptionController but I need the payload and the url which throwed this exception , Is it possible to get that data.
Hope this helps. This code is straight from default Grails installation
<g:if test="${Throwable.isInstance(exception)}">
<g:renderException exception="${exception}" />
</g:if>
<g:elseif test="${request.getAttribute('javax.servlet.error.exception')}">
<g:renderException exception="${request.getAttribute('javax.servlet.error.exception')}" />
</g:elseif>
<g:else>
<ul class="errors">
<li>An error has occurred</li>
<li>Exception: ${exception}</li>
<li>Message: ${message}</li>
<li>Path: ${path}</li>
</ul>
</g:else>
in your exceptionController (when a 500 happened) you should be able to access the original URL via
request['javax.servlet.error.request_uri']
additionally you can get the requested URL in every controller via
request[RequestDispatcher.FORWARD_REQUEST_URI]
for accessing the request body after it has been consumed, you could use a solution as suggested in
Accessing the raw body of a PUT or POST request but bear in mind that this of course has to keep the body in memory.
to get the originally called controller and action name inside your exceptionController, the only solution I know right now would be either:
class ExceptionController {
def grailsUrlMappingsHolder
internalServerError() {
// request['javax.servlet.error.request_uri'] sometimes returns null?
def url = request['javax.servlet.error.request_uri'] ?: request[RequestDispatcher.FORWARD_REQUEST_URI]
def originalCall = url ? grailsUrlMappingsHolder.match(request['javax.servlet.error.request_uri'])?.paramss : [:]
def controller = original?.controller
def action = original?.action
...
}
}
alternatively, by saving the first controller call in a filter like that:
class SaveFirstCallFilter {
def filters = {
all(controller:'*', action:'*') {
before = {
// don't want to overwrite when forwarding or including other actions
if (!request['SAVED_CONTROLLER']) {
request['SAVED_CONTROLLER'] = controllerName
request['SAVED_CONTROLLER'] = actionName
}
}
}
}
}
Related
I'm converting a grails 2.4.4 app to 3.1.8.
In some of my controller methods sometimes (based on what happened in the service call) I'll just set a message in the flash and nothing else, this was fine in 2.4.4, the screen would be re-rendered with the flash message but in 3.1.8 nothing is rendered at all, the screen in totally blank.
Seemingly if I add a statement after setting the message in the flash the screen is rendered, the statement can be anything e.g. println 'hello' or return or new ModelAndView().
Example below:
def index() {
def res = myService.whatever()
if (res) {
[res: res]
}
else {
flash.message = message( code: 'no.res' ) // if we get here nothing is rendered
}
}
Is this a change to grails 3 or am I missing something somewhere?
Thanks
Try doing this
def index() {
def res = myService.whatever()
[res: res]
}
Then in the index.gsp view
<g:if test="${res}">
<!-- cool thing goes here -->
</g:if>
<g:else>
<g:message code="no.res"/>
</g:else>
I've got this method on my controller, which basically validates and saves entity or renders errors if any:
def initCreateProduct() {
render view: "/admin/product/createProduct"
}
def createProduct(params) {
def product = new Product(params)
if (product.validate()) {
flash.message = "product.createSuccess"
product.save(failOnError: true);
redirect action: "initCreateProduct"
} else {
render view: "/admin/product/createProduct", model: [product: product]
}
}
It works fine in case if validation is successfull (URL in browser is "product/initCreateProduct", which is ok), but in case if validation fails my URL changes to "product/index", which is wrong. What I need is to get the same URL in both cases (it's the same page after all). Thanks in advance.
Update: finally solved it.
The problem is caused by used in my gsp, which basically changes action url to index, and sends real action as post-method parameter. To save action in url I have used instead of
It is doing a controller redirect and not being able to resolve an URL. IIRC, grails will pass additional parameters to the index method to find that action.
If you do something like this, it won't do it:
if (product.validate()) {
flash.message = "product.createSuccess"
product.save(failOnError: true)
initCreateProduct()
}
I have a running tomcat at localost.
I wish to write a grails filter such that when ever user goes to localhost/filter/ intercept the call and do some processing. I created a filter which is inside conf folder
class TestFilters {
def filters = {
filter(uri:'/filter') {
before = {
}
after = {
}
afterView = {
}
}
}
}
after setting this up when I go to localhost/filter/ I get only 404 error.
Where I'm making the mistake?
Thanks in advance
If you have no FilterController the url localhost/filter has no ressource to show - so you get a 404 error. You have to adapt your UrlMappings so that localhost/filter is a valid url of application.
Add the following to UrlMappings.groovy:
"/filter" (controller: "yourController", action:"yourAction")
Now - the url localhost/filter points to yourAction in yourController and the filter should be applied.
At the end of my save action I redirect to the show action like this:
redirect(action: "show", id: exampleInstance.id)
In my show action I want to be able to detect if someone came directly to this action via a url, or if they were redirected here from another action. I tried request.isRedirected() but it always returns false.
How do I detect if I am in an action as the result of a redirect from another action?
I guess you want to display a confirmation message. Grails has a built-in feature for this kind of use-case:
http://www.grails.org/doc/2.1.0/ref/Controllers/flash.html
Have a look at the example:
class BookController {
def save() {
flash.message = "Welcome!"
redirect(action: 'home')
}
}
In the view you can print or check against flash.message.
In theory, isRedirect checks request attributes. It is equivalent to
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
if(request.getAttribute(GrailsApplicationAttributes.REDIRECT_ISSUED) != null){
println "request was redirect"
}
Try it directly and tell me what happens.
It looks like in the Grails source that isRedirected() is only applicable to the save action, as it gets set in the redirect() method logic, and so wouldn't be set in the show action.
Instead, here are some manual options. One is to add a flag to the flash object, which is then tested in the redirect action. Since it is in flash scope it will be cleared at the end of the show action:
def save() {
// Do stuff
flash.redirectFrom = "save"
redirect(action:"show")
}
def show() {
if (flash.redirectFrom) {
// Respond to redirect
}
// Do other stuff
}
Another option is to issue a chain() call instead of a redirect() and test for the implicit chainModel object. The chainModel won't exist when the show action is requested from an external URL:
def save() {
// Do stuff
chain(action:"show",model:[from:'show'])
}
def show() {
if (chainModel) {
// Respond to redirect
}
// Do other stuff
}
I'm learning Grails so forgive me if I'm missing something basic.
I'm trying to create a wizard/web flow using the Grails Web Flow plugin. I'd like for the first step of the flow to render some variables. From what I've read about normal controllers, typically this would be done by passing those variables from the controller to the view using a map. In the WebFlow model though, I don't know where to initialize these variables and how to pass them to the first step. I tried creating an initialize "action" and putting the variable into the flash scope, knowing that it should pass through one redirect, but it doesn't render on the gsp.
How is this done?
Here's a snip of the controller, which prints "4" in the console:
class ServicesController {
def index() {
redirect(action: "initialize")
}
def initialize() {
flash.assessmentTypes = AssessmentType.list()
println flash.assessmentTypes.size
redirect(action: "request")
}
def requestFlow = {
selectAssessments {
on("next") {
// capture assessments
}.to("productInfo")
on("cancel").to("finish")
}
...
And a snip of the gsp, which throws a nullpointer when rendering the size:
${flash.assessmentTypes.size}
<g:each var="assessmentType" in="${flash.assessmentTypes}">
<li><g:checkbox name="assessmentType" value="${assessmentType.id}" />${assessmentType.name}</li>
</g:each>
No problem...
Use a flow initializer to act as the first step in the flow and then move it to the first step on success of the initFlow.
def wizardFlow = {
initFlow {
flow.assessmentTypes = AssessmentType.list(); //<-- put them in the flow so you can access it in step1.gsp
}
on('success').to('step1')
on(Exception).to('handleFlowError')
step1{
on('next'){
flow.asessmentType = AssessmentType.get(params.assessmentType.id);
println("They picked ${flow.asessmentType}.");
}.to('step2')
on('exit').to('exit')
}
step2{
on('next'){ /* do stuff */ }.to('finish')
on('previous').to('step1')
on('exit').to('exit')
}
exit( /* exit flow before finish */ )
finish( /* finish up */ )
handleFlowError( */ not good :( */)
}
step1 GSP....
<g:select name="assessmentType.id" from="${assessmentTypes}" optionKey="id" value="${assessmentType?.id}" />
This is untested but it should work just fine. Enjoy :)