Best way to pass objects between controller actions in grails - spring

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.

Related

Restricting auto Help Page contents when using Attribute Routing in Web API 2

I'm currently implementing a Web API using Web API 2's attribute routing (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2). I am also using the Help Pages module in order to automatically generate documentation from XML comments (http://www.asp.net/web-api/overview/creating-web-apis/creating-api-help-pages).
For this API I am providing support for optional return format extensions, so that every API method has a pair of routes defined on it like so:
[HttpGet]
[Route("Path/Foo")]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFoo()
{
// Some API function.
}
This allows a user to hit any of these and get a result:
www.example.com/api/Controller/Path/Foo
www.example.com/api/Controller/Path/Foo.json
www.example.com/api/Controller/Path/Foo.xml
My issue is that when Help Pages uses MapHttpAttributeRoutes() to generate documentation, it is picking up both routes for each method. So right now I see help for:
api/Controller/Foo
api/Controller/Foo.{ext}
But I want to only see:
api/Controller/Foo.{ext}
I would prefer to hide the non-extension route on each method, so that every method only shows a single Help Page entry.
Has anyone else tried something similar? Is there a work around that I am missing?
My question would be is that, would consumers of your api figure out easily that the {ext} is optional?...personally, I would prefer the default behavior...but anyways following are some workarounds that I can think of:
A quick and dirty workaround. Split the DoFoo into 2 actions like DoFoo() and DoFooWithExt maybe. Notice that I am using an attribute called ApiExplorerSettings, which is for HelpPage purposes. Example below:
[HttpGet]
[Route("Path/Foo")]
[ApiExplorerSettings(IgnoreApi=true)]
public HttpResponseMessage DoFoo()
{
return DoFooHelper();
}
[HttpGet]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFooWithExt()
{
return DoFooHelper();
}
private HttpResponseMessage DoFooHelper()
{
//do something
}
Create a custom ApiExplorer (which HelpPage feature uses internally) and check for specific routes like the following and can decide whether to show the action or not for that particular route.
// update the config with this custom implementation
config.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(config));
public class CustomApiExplorer : ApiExplorer
{
public CustomApiExplorer(HttpConfiguration config) : base(config)
{
}
public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
{
if (route.RouteTemplate.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);
}
}
Get list of all ApiDescription from the default ApiExplorer and then filter out the descriptions which you do not like. Example:
Configuration.Services.GetApiExplorer().ApiDescriptions.Where((apiDesc) => !apiDesc.RelativePath.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))

grails 2.2.2 platform-core-plugin No signature of method event in domain model

I try out the platform-core-1.0 rc5 Plugin to services by events. Now I write a service in the grails-plugin "listadmin":
package listadmin
class SECO_ListenService {
#grails.events.Listener(topic='getEntriesOfList', namespace='listadmin')
def getEntriesOfList(String intnalListName) {
println "SECO_ListenService"
def Liste aList = Liste.findByInternal_name(intnalListName)
return aList.eintrage.toList()
}
}
This service should return a list for dropdown in an other grails-plugin called "institutionadmin". I want to use this list of the service for a dropdown of a domain-model. I should mention that I use dynamic scaffolding. Now I try to call this event in the domain-model:
package institutionadmin
import org.springframework.dao.DataIntegrityViolationException
class Einrichtung {
Long einrichtungs_type
Long type_of_conzept
int anzahl_gruppen
int anzahl_kinder_pro_Gruppe
String offnungszeiten
static hasMany = [rooms : Raum]
static constraints = {
def aList = []
def reply = event(for:"listadmin", topic:"getEntriesOfList", data:"einrichtung_type").waitFor()
aList = reply.value.toList()
einrichtungs_type(inList: aList)
}
}
If I try to run this application i get the following error:
Caused by MissingMethodException: No signature of method: institutionadmin.Einrichtung.event() is applicable for argument types: (java.util.LinkedHashMap) values: [[for:listadmin, topic:testEventBus]]
Possible solutions: ident(), every(), every(groovy.lang.Closure), count(), get(java.io.Serializable), print(java.lang.Object)
If call this event in a controller everything is fine and the documentation of this plugin describe that I can call events also in domain-models and services... This error-method tell me, that the class don't know the event method.
Do I have to configure anything else?
Should call the event in another way or where is my mistake?
Has anybody experiences with this module?
The event(...) dynamic methods are not available on class (static) level.
You can pull the grailsEvents spring bean and call its event() method alternatively. You still have to get the bean from the application context statically though.
You could also use a custom validator instead, as you can get the current domain instance as a parameter, which should have the event() method injected.
something like this :
static myList = []
static constraints = {
einrichtungs_type validator: { value, instance ->
if(!myList){
// cache it the first time you save/validate the domain
// I would probably recommend you NOT to do this here though in
// real life scenario
def reply = instance.event('blabla').get()
myList = reply.value.toList()
}
return value in myList
}
}
Anyway, In my case I would probably load the list elsewhere (in the Bootstrap.groovy for instance) and use it / inject it in my domain instead of doing in the constraints closure.
I faced similar kind of problem, I wanted to use the event call inside a service class which is going to call the listener in other service class. When I started my application I got the same error.What I did was, added the plugin(platform-core:1.0.RC5) entries in BuildConfig.groovy like below
plugins {
build(":tomcat:$grailsVersion",
":platform-core:1.0.RC5") {
export = false
}
compile ':platform-core:1.0.RC5'
runtime ':platform-core:1.0.RC5'
}
Then I ran grails > clean and grails > compile on that project and restarted the server.It started working. Might be you can give a try.

Using only a controller in FW1 without a view

I have an Ajax request that sends some data to a page and expects back a truthy or falsey value depending on if the data was saved. In my controller I do everything and set the content to a true or false value. I really don't want to create a view just to output 1 variable, so I was wondering if there was a way that I don't have to use a view and only use the controller to output simple strings.
I believe you cannot disable views completely, but there's a pretty simple workaround: you can create one view and use it for many actions.
Let's say we've created the view views/main/ajax.cfm, what could be inside it? Obviously, simplest way is:
<cfoutput>#HTMLEditFormat(rc.response)#</cfoutput>
Personally I like returning JSON, it allows me to have status field, plus data, if needed. This way my view looks like this:
<cfheader name="Content-Type" value="application/json" />
<cfoutput>#SerializeJSON(rc.response)#</cfoutput>
Any way, now in our action we need to do something like this:
// prevent displaying the layout
request.layout = false;
// force special view
variables.fw.setView("main.ajax");
// init response (according to the choice made earlier)
rc.response["status"] = "OK";
rc.response = "";
There's one more gotcha for this. Sometimes you don't want AJAX page to be accessed directly (like opened in browser), or vise-versa -- want to do some debugging when it is.
There's a cool helper isAjax in CFWheels framework, it is easy to port to the FW/1. It could be as simple as adding method like this to controller:
/*
* Check if request is performed via AJAX
*/
private boolean function isAjax() {
return (cgi.HTTP_X_REQUESTED_WITH EQ "XMLHTTPRequest");
}
Actually, that setup code above is also helper method in my apps:
/*
* Set up for AJAX response
*/
private struct function setAjax() {
// prevent displaying the layout
request.layout = false;
// force special view
variables.fw.setView("main.ajax");
local.response["status"] = "OK";
return local.response;
}
So in my action code whole check looks like this, which is pretty compact and convenient:
if (isAjax()) {
rc.response = setAjax();
}
else {
return showNotFound();
}
Hope this helps.
You can't output directly from a Controller: its job is just to call the Model and pass data to the View, so you'll need a view template to do the outputting.
However, you can avoid having to create a separate view for each controller method by using the framework's setView() method. This allows you to override the convention and apply a single view to multiple controller methods. So you could set up a generic "ajax view" and then use it to output the data from any of your controllers:
views/main/ajax.cfm
<!---Prevent any layouts from being applied--->
<cfset request.layout=false>
<!--- Minimise white space by resetting the output buffer and only returning the following cfoutput --->
<cfcontent type="text/html; charset=utf-8" reset="yes"><cfoutput>#rc.result#</cfoutput>
controller.cfc
function init( fw )
{
variables.fw=arguments.fw;
return this;
}
function getAjaxResponse( rc )
{
rc.result=1;
fw.setView( "main.ajax" );
}
function getAnotherAjaxResponse( rc )
{
rc.result=0;
fw.setView( "main.ajax" );
}
You can use onMissingView in you Application.cfc to handle the response for ajax calls, this way you don't need to perform any extra logic in your controller methods.
// Application.cfc
function onMissingView(rc) {
if(structKeyExists(rc, "ajaxdata") && isAjaxRequest()) {
request.layout = false;
content type="application/json";
return serializeJSON(rc.ajaxdata);
}
else {
return view("main/notfound");
}
}
function isAjaxRequest() {
var headers = getHttpRequestData().headers;
return structKeyExists(headers, "X-Requested-With")
&& (headers["X-Requested-With"] eq "XMLHttpRequest");
}
// controller cfc
function dosomething(rc) {
rc.ajaxdata = getSomeService().doSomething();
}
This checks if the request context has an ajaxdata key, and is a genuine ajax request, then returns the serialize data. If it doesn't then it renders the main.notfound view

Return an object and control status code

I upgraded from the ASP.NET Web API release on nuget.org (the version before the RC on Friday) to the nightly builds on myget.org. As expected there were many breaking changes, and one of them I can't seem to get around: We have a scenario where we want our action to return an object and set the status code to 201 Created. This was quite easily done before (might not compile - conceptual code from the top of my head):
Session session = GetSessionInfo(requestMessage);
var response = new HttpResonseMessage(HttpStatusCode.Created);
response.Content = response.CreateContent(session);
return response;
CreateContent was actually an extension method located in System.Net.Http.HttpResponseMessageExtensions calling an internal constructor in ObjectContent. With the new release HttpResponseMessageExtensions seems to be gone in the new release and so does the internal constructors of ObjectContent. Now it seems I must call an ObjectContent constructor, and the following seems to be the most appropriate for our needs:
public class ObjectContent<T> : ObjectContent
{
public ObjectContent(T value, MediaTypeFormatter formatter)
{
}
}
However it seems I have to pass a MediaTypeFormatter into it, mixing content negotiation into the logic of the action. In our setup content negotiation is generic and completely decoupled from the controllers.
Does anyone have a suggestion for solving the scenario to return an object, set the response status code, but not have to deal with MediaTypeFormatter, media type or any other content negotiation related stuff?
We redesigned how ObjectContent<T> creation works. Starting with the RC, you should be calling the CreateResponse() set of extension methods (they're off HttpRequestMessage). This will actually produce an instance of HttpResponseMessage with an instance of a content-negotiated ObjectContent<T> inside. So, something like:
Session session = GetSessionInfo(requestMessage);
var response = Request.CreateResponse(HttpStatusCode.Created, session);
return response;

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

Resources