Grails filters: Any way of chaining filters? - spring

Is there a way to chain several filters in a grails application (as in Java filters)? Maybe something with spring?
I've written a couple of filters, and would like to get them to execute serially (order is not particularly important). The reason behind this? I need to write about 20, 30 filters, and don't want them all in the same file.
I've read about Spring's DelegatingFilterProxy but can't figure out on how to configure it to chain all my grails filters.

Since Grails 1.3.1 you can chain filters by using the "dependsOn" keyword:
def dependsOn = [MyOtherFilters.class]
http://jira.codehaus.org/browse/GRAILS-6229

I may not be understanding the core issue here, but the simple answer might be "they're already chained". Filters are executed based on the selector you put in the filter closure (e.g. myPreProcessorFilter(controller:'', action:'') {}). All selectors that match your controller/action will execute. I do this all the time with logging and performance measurement filters.
Here's an example. Both the logAction and measureMethodTime filters will be applied to all controllers and actions (since I left the selector wide open).
import org.springframework.web.context.request.RequestContextHolder as RCH
import com.x.y.*
class PerformanceFilters {
def filters = {
logAction(controller:'*', action:'*'){
before = {
log.debug("${controllerName}.${actionName}: entering; params=${params}")
}
}
measureMethodTime(controller:'*', action:'*'){
before = {
def session = RCH.currentRequestAttributes().getSession(false)
if (session)
{
Q.startTimer("${session.id}-${controllerName}-${actionName}", "method.${controllerName}.${actionName}")
}
}
afterView = {
def session = RCH.currentRequestAttributes().getSession(false)
if (session)
{
Q.stopTimer("${session.id}-${controllerName}-${actionName}", "method.${controllerName}.${actionName}")
}
}
}
}
}

http://grails.org/doc/latest/guide/single.html#6.6.4%20Filter%20Dependencies

Related

How to restrict a webapi controller method invokation using a filter

I have several controllers, and each of them has operations that represents
REST endpoints.
i want to invoke those operations based on conditions
can it be possible to achieve it using filters with attributes and not using (if..else) statements in all operations?
if so, can you please provide a skeleton of how doing it?
thanks.
If it is asp.net core then check please this link https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2
You can do something like this
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
if (...)
{
// do something before the action executes
var resultContext = await next();
// do something after the action executes; resultContext.Result will be set
}
}
}

use camel case serialization only for specific actions

I've used WebAPI for a while, and generally set it to use camel case json serialization, which is now rather common and well documented everywhere.
Recently however, working on a much larger project, I came across a more specific requirement: we need to use camel case json serialization, but because of backward compatibility issues with our client scripts, I only want it to happen for specific actions, to avoid breaking other parts of the (extremely large) website.
I figure one option is to have a custom content type, but that then requires client code to specify it.
Is there any other option?
Thanks!
Try this:
public class CamelCasingFilterAttribute : ActionFilterAttribute
{
private JsonMediaTypeFormatter _camelCasingFormatter = new JsonMediaTypeFormatter();
public CamelCasingFilterAttribute()
{
_camelCasingFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
ObjectContent content = actionExecutedContext.Response.Content as ObjectContent;
if (content != null)
{
if (content.Formatter is JsonMediaTypeFormatter)
{
actionExecutedContext.Response.Content = new ObjectContent(content.ObjectType, content.Value, _camelCasingFormatter);
}
}
}
}
Apply this [CamelCasingFilter] attribute to any action you want to camel-case. It will take any JSON response you were about to send back and convert it to use camel casing for the property names instead.

Best way to pass objects between controller actions in grails

I want a link to open up another view in my webapp to display information about the specified object.
What is the best way to pass objects between controllers actions in grail?
Actions can be chained using the chain controller method.
Chaining allows the model to be retained from one action to the next.
The earlier answers are incomplete. So, I am compiling them along with my inputs and making them clearer.
You have two options:
Chaining the actions:
def action1() = {
DomainClass domainInstance = DomainClass.findById(params.id);
chain (action: 'action2', model: [domainInstance: domainInstance]);
}
def action2() = {
DomainClass domainInstance = chainModel?.domainInstance ?: DomainClass.findById(params.id);
[domainInstance: domainInstance];
}
However, the successor action seems to use a fresh database session
instead of reusing that of the predecessor (which may also be
configurable in Grails, I don't know how though). So any lazily
loaded entity may not be fully loaded in the successor action and
may give LazyInitializationException (depending on your ORM configuration of course).
Forwarding the request:
def action1() = {
DomainClass domainInstance = DomainClass.findById(params.id);
forward (action: 'action2', model: [domainInstance: domainInstance]);
}
def action2() = {
DomainClass domainInstance = request?.domainInstance ?: DomainClass.findById(params.id);
[domainInstance: domainInstance];
}
Unlike in the previous case, request forwarding reuses the existing session so lazy loading issues will not occur.
As you can see, the syntax for both is almost identical. But one should prefer request forwarding as per the requirement in question due to the issue mentioned above. Another important detail is regarding the URL viewed in the address bar on/after page loading. Forwarding the requests will PRESERVE the page URL while chaining the actions will CHANGE the page URL to that of the latest action.
(Late to the party, but...) I'm using Grails 2.4.4, which allows me to do the below:
def usernameLogin() {
SecurityToken securityToken = authService.loginWithUserPass(params.user, params.pass)
chain action: 'afterLogin', model: [securityToken: securityToken]
}
def ssoLogin() {
SecurityToken securityToken = authService.ssoLogin(params.remoteUser, params.key)
chain action: 'afterLogin', model: [securityToken: securityToken]
}
def afterLogin() {
SecurityToken securityToken = (SecurityToken) chainModel['securityToken']
if (securityToken.valid) {
forward action: 'loggedInRedirect'
}
else {
forward action: 'loginFailed'
}
}
SecurityToken is an object that contains string and enum
The key is 1) using "chain action" in source action, 2) using chainModel in target action
Hope this helps.

How to use Spring AOP aspects with Groovy and Grails, specific caching example

We built a large insurance policy and claim management system using Grails and Groovy. Performance problems are slowing down the site because all 'READS' fetch from the database, which is not necessary since most data is static. We want to introduce a simple key/value cache in the Grails layer, but we don't want to litter the existing code with cache.get() and cache.set() code, we want to use aspects instead.
Here is a sample from our main controller....
InsuranceMainController {
def customer {
//handles all URI mappings for /customer/customerId
}
def policy {
//handles all URI mappings for /policy/policyId,
}
def claim {
//handles all URL mappings for /claim/claimId
}
As far as the cache goes, assume for the moment it's a simple Map named "cache" that's available as a globally-scoped object, and objects in the cache are keyed by request URI...
cache.put("/customer/99876", customerObject)
cache.put("/policy/99-33-ARYT", policyObject)
Going back to the controller, if we just litter the code with cache.get()/set(), which is what we want to avoid using Spring AOP, we'll end up with messy code. We want to achieve the following functionality with apsects, or with just a simpler and cleaner implementation...
InsuranceMainController {
def customer {
Object customer = cache.get(request.getRequestURI())
if ( customer != null)
//render response with customer object
}else
//get the customer from the database, then add to cache
CustomerPersistenceManager customerPM = ...
customer = customerPM.getCustomer(customerId)
cache.put(request.getRequestURI(), customer)
}
}
We need examples that show how we can achieve the above functionality using Spring AOP or something simpler in Grails while avoiding the littering of the code with cache.get()/set(). Suggestions to refactor the existing controller are welcome if it's required to get AOP working properly.
Thanks in advance
Rather than using AOP, you could adapt Mr Paul Woods' controller simplification pattern to move the cache handling out to a single method?
Something like this might work:
class InsuranceMainController {
def customer = {
Object customer = withCachedRef( 'customerId' ) { customerId ->
CustomerPersistenceManager customerPM = ...
customerPM.getCustomer(customerId)
}
}
def policy = {
//handles all URI mappings for /policy/policyId,
Object policy = withCachedRef( 'policyId' ) { policyId ->
PolicyPersistenceManager policyPM = ...
policyPM.getPolicy(policyId)
}
}
// ...
private def withCachedRef( String id, Closure c ) {
Object ret = cache.get( request.requestURI )
if( !ret ) {
ret = c.call( params[ id ] )
cache.put( request.requestURI, ret )
}
ret
}
}
However, I haven't tested it at all :-( Just a suggestion of an alternative to AOP

how to selectively set a property using DEPENDENCY INJECTION in a grails service for unit testing

EDIT: Please let me be clear, I'm asking how to do this in Grails using Spring Dependency Injection, and NOT Grails' metaclass functionality or new().
I have a grails service that is for analyzing log files. Inside the service I use the current time for lots of things. For unit testing I have several example log files that I parse with this service. These have times in them obviously.
I want my service, DURING UNIT TESTING to think that the current time is no more than a few hours after the last logging statement in my example log files.
So, I'm willing to this:
class MyService {
def currentDate = { -> new Date() }
def doSomeStuff() {
// need to know when is "right now"
Date now = currentDate()
}
}
So, what I want to be able to do is have currentDate injected or set to be some other HARDCODED time, like
currentDate = { -> new Date(1308619647140) }
Is there not a way to do this with some mockWhatever method inside my unit test? This kind of stuff was super easy with Google Guice, but I have no idea how to do it in Spring.
It's pretty frustrating that when I Google "grails dependency injection" all I find are examples of
class SomeController {
// wow look how amazing this is, it's injected automatically!!
// isn't spring incredible OMG!
def myService
}
It feels like all that's showing me is that I don't have to type new ...()
Where do I tell it that when environment equals test, then do this:
currentDate = { -> new Date(1308619647140) }
Am I just stuck setting this property manually in my test??
I would prefer not to have to create a "timeService" because this seems silly considering I just want 1 tiny change.
Groovy is a dynamic language, and as such it allows you to do almost what you're asking for:
class MyServiceTests extends GrailsUnitTestCase {
def testDoSomeStuff() {
def service = new MyService()
service.currentDate = { -> new Date(1308619647140) }
// assert something on service.doSomeStuff()
}
}
Keep in mind this only modifies the service instance, not the class. If you need to modify the class you'll need to work with the metaClass. Take a look at this post by mrhaki.
Another option would be to make the current date a parameter to doSomeStuff(). That way you wouldn't need to modify your service instance.
Thanks for the help guys. The best solution I could come up with for using Spring DI in this case was to do the following in
resources.groovy
These are the two solutions I found:
1: If I want the timeNowService to be swapped for testing purposes everywhere:
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
if (GrailsUtil.environment == 'test') {
println ">>> test env"
timeNowService(TimeNowMockService)
} else {
println ">>> not test env"
timeNowService(TimeNowService)
}
}
2: I could do this if I only want this change to apply to this particular service:
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
if (GrailsUtil.environment == 'test') {
println ">>> test env"
time1(TimeNowMockService)
} else {
println ">>> not test env"
time1(TimeNowService)
}
myService(MyService) {
diTest = 'hello 2'
timeNowService = ref('time1')
}
}
In either case I would use the service by calling
timeNowService.now().
The one strange, and very frustrating thing to me was that I could not do this:
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
if (GrailsUtil.environment == 'test') {
println ">>> test env"
myService(MyService) {
timeNow = { -> new Date(1308486447140) }
}
} else {
println ">>> not test env"
myService(MyService) {
timeNow = { -> new Date() }
}
}
}
In fact, when I tried that I also had a dummy value in there, like dummy = 'hello 2' and then a default value of dummy = 'hello' in the myService class itself. And when I did this 3rd example with the dummy value set in there as well, it silently failed to set, apparently b/c timeNow blew something up in private.
I would be interested to know if anyone could explain why this fails.
Thanks for the help guys and sorry to be impatient...
Since Groovy is dynamic, you could just take away your currentDate() method from your service and replace it by one that suits your need. You can do this at runtime during the setup of your test.
Prior to having an instance of MyService instantiated, have the following code executed:
MyService.metaClass.currentDate << {-> new Date(1308619647140) }
This way, you can have a consistent behavior across all your tests.
However, if you prefer, you can override the instance method by a closure that does the same trick.
Let me know how it goes.
Vincent Giguère

Resources