I am trying to add a custom HTTP header to the response of a certain set of controllers using Grails Filters:
class ApiFilters {
def filters = {
xwingFilter(uri:'/api/**') {
after = {
response.setHeader('X-Wing', 'foo')
}
}
}
}
If the controller renders a view or text, the header gets added to the response.
If the controller uses "render as JSON" the header is not added. It looks like the JSON converter is closing the output stream and sending it right away. Is that right?
I could use Grails interceptors but I'd have to replicate it in more than one controller.
Any ideas on how can I achieve this?
You can do the translation from model to JSON in the filter instead of the action:
controller:
class myController {
def myAction() {
[aThing: 1, anotherThing: [a: 1, b: 2]]
}
}
filter:
class ApiFilters {
def filters = {
xwingFilter(uri:'/api/**') {
after = { model ->
response.setHeader('X-Wing', 'foo')
render model as JSON
return false // prevent normal view from rendering
}
}
}
}
Related
Grails 2.4.4 here. All of my controllers were generated using the standard grails create-controller ... command:
class FizzController {
def index() {
// ...
}
}
class BuzzController {
def index() {
// ...
}
}
...etc.
At the top of each action method, in each controller, I need to call a method doSomething(String). This method needs to have access to redirect(...) so that it can redirect the user if need be. Ideally, I could create an abstract base controller class and have all my controllers extend it:
abstract class BaseController {
void doSomething(String cmd) {
if(cmd == 'Yee haw!') {
redirect(controller: 'foo', action: 'baz')
return false
}
}
}
class FizzController extends BaseController {
def index() {
String cmd = getCmdSomehow()
doSomething(cmd)
// ...etc.
}
}
class BuzzController extends BaseController {
def index() {
String cmd = getCmdSomehow()
doSomething(cmd)
// ...etc.
}
}
However I'm not sure Grails will allow this, since it's doing "Groovy magic" under the hood to make sure there is access to things like redirect(...) and render(...), etc. It would also be wonderful if I could put all this in a closure and just have it executed implicitly (without having to call doSomething() at the beginning of each and every controller action.
What is my best option/solution here? Please use a concrete code example!
Use Filter instead:
class ExampleFilters {
def filters = {
logFilter(controller: '*', action: '*') {
before = {
}
after = { Map model ->
}
afterView = {
}
}
}
}
I'm trying to render a .GSP view inside the view folder from my filter. The following code show that:
def filters = {
all(controller:'*', action:'*') {
afterView = { Exception e ->
if (controllerName) {
//some code here
if (annotation!=null) {
switch(response.format){
case 'all':
if(!response.containsHeader("AC_MSG")|| !response.containsHeader("AC_STATUS")){
render(view: "/internalerror", model: [controller: controllerName,action:currentAction,
message:"Response doesn't contain required headers AC_MSG or AC_STATUS. Either add the required headers or use json format.",
example:"Add the following response headers: AC_MSG:response message , AC_STATUS: false or true"
])
return false
}
break
default:
render status: 406
break
}
}
}
}
}
}
The problem is that this page didn't get rendered even the code is executed. The page is on the view directory directly. What I did wrong?
Thanks,
I don't think a filter can render a gsp, but controllers can.
A perfect example of what you want to do is available in the docs: filters
Basically you create an action inside a controller that renders the page, and the filter just redirects to the action.
case 'all':
if(!response.containsHeader("AC_MSG")|| !response.containsHeader("AC_STATUS")) {
redirect(controller: "someController", action:"someAction")
return false
}
Make ErrorController.groovy and implement action with this render view and params.
In filter use only redirect. Remove 'return false' statement also.
I have a Grails 2.1.1 application which runs fine, at least until I try to define a filter for all Controller to redirect to the index.gsp if user is not set in the session variable...
what ever I try, I'm not able to redirect to "/index", nor render "/index" when starting the server - if I remove the filter and redirect to "/index" from my AuthenticationController on false parameters, all works like charm...
so, here's what I have so far:
class AuthenticationFilters {
all(controller:'*', action'*') {
before = {
def user = (User) session.getValue("user")
if(user == null || !user.loginState) {
redirect(controller: 'authentication', action: 'index')
return false
}
}
}
}
class AuthenticationController {
def authenticationService
def index = {
render(view: '/index')
}
def login(LoginCommand cmd) {
if(cmd.hasErrors()) {
redirect(action: index)
return
}
}
....
}
now, if I comment out the all Filters definition, everything works well. I got the page (index.gsp) shown on start up and if the LoginCommand has errors, I'm redirected to the index.gsp page without any problems.
if I now comment in the all Filters definition, I get a 404.
I tried:
Grails: Redirect to index.gsp that is not in any controller
Upgrade to Grails 2.0: /index.gsp not found
Grails: what are the main issues (and his solutions) when deploying in weblogic?
but I didn't had any luck...
I'm developing on Intellij IDEA 11.1.4 Premium (evaluation)
EDIT: I tried to get the User object from the session property in my AuthenticationFilters class, surrounded by a try/catch block and now facing the problem that obviously the session property is not available? why?
try {
def user = (User) session.getValue("user")
if((user == null || !user.loginState)) {
...
}
} catch(Exception e) {
println("... couldn't get user from session! ${e.getMessage()}")
}
console output:
... couldn't get user from session! No such property: session for class: grailstest001.AuthenticationFilters
any suggestions on this?
So, just to add my experience here to close this question as solved and for further usage for other users:
to check if the user is entering the page for the first time, you could easily do this by checking for the controllerName field. It should be null or "" (empty String) if the user was not referred to the site by any controller.
Also, I'm not using any Database within my application for authentication because all of this issuses are backended by an API. So I created a UserService.groovy class which is acting as a SessionScopedBean and I store all my user related infos within this class.
So, your filter definition could look like:
class MyFilters {
def filters = {
before = {
if(!controllerName || controllerName.equals("")) {
redirect(controller:'home',action:'index')
}
if(!applicationContext.userService?.getUser() || !applicationContext.userService?.getUser.isLoggedIn) {
redirect(controller:'auth',action:'login')
}
}
}
}
Otherwise, if you don't want to redirect the user which 'freshly' entered your page from within your Filters.groovy class, you could use the UrlMappings.groovy class to do so. Just map your / (root) index to the page you want:
class UrlMappings {
static mappings = {
"/"(controller:'mycontroller',action:'myaction') // change it to your liking
"/$controller/$action?/$id?" {
constraints {
// apply constraints here
}
}
"500"(view:'/error')
}
}
I think your filter syntax may be incorrect - try
Class AuthenticationFilters {
def filters = { // <--- added this
all(controller:'*', action'*') {
before = {
def user = (User) session.getValue("user")
if(user == null || !user.loginState) {
redirect(controller: 'authentication', action: 'index')
return false
}
}
}
} // <-- added
}
I need to change the gsp to render dynamically according certain params. the thing is: in the render action I get
org.codehaus.groovy.grails.commons.metaclass.PropertyExpression#680dc2a instead of the params I passed.
promoFlow = {
start {
action {
flow.inputPage = "landing1/input/${params.land}"
flow.pinPage = "landing1/pin/${params.land}"
flow.finishPage = "landing1/finish/${params.land}"
...
success()
...
}on('success'){
...
}.to 'preview'
}
preview {
render(view: flow.inputPage )
on('next') {...}.to 'pin'
}
Paul if you are using Grails2 why not add a parameter to the action method instead of accessing the params directly, eg:
def myAction(String land) {
...
flow.inputPage = "landing1/input/${land}"
...
}
class SearchController {
def list = {
List<Product> productsList = productRepository.findProductBySearchPhrase(params.searchPhrase)
render(view: "/product/list", model: [products: productsList])
}
}
class UrlMappings {
"/$controller/$action?/$id?" {
constraints {}
}
"/search" {
controller = "search"
view = "list"
constraints {}
}
}
1) This URL works properly, rendering GSP from /views/product/list directory.
myapp.com/search/list?searchPhrase=underware
2) This URL doesn't do the work, rendering /views/search/list.
myapp.com/search?searchPhrase=underware
Any ideas?
May be you want to replace 'view' with 'action' in the search URL Mapping.