Using WebObjects DirectActions – pt. 2

At the conclusion of part one of this mini-series we had successfully configured a WebObjects application to accept DirectActions, had written a simple DirectAction method, and created a WOHyperlink to trigger it. Now it’s time to look at some of the common additional requirements you might encounter. As I mentioned last time they are:

  1. Passing parameters to our action
  2. Accessing a database with our action
  3. Invoking or using a Session with our action

Each of these will take a little work to get through, so I’m going to tackle them in several separate posts. Today we will look at passing parameters to our actions. We are going to extend the examples we used last time so fire up your IDE (Eclipse I hope) and lets get started.

Passing parameters to a DirectAction

Being able to trigger an action based on the URL is fun and all, but often we will want to do more. More? what could we possibly want that is more? Well submitting a form to a direct action URL might be nice, or passing a value to the next page for display or to modify its behaviour would be cool too.

To get this to work we have a number of issues to cover:

  1. Handling parameters in our action
  2. Passing a single parameter to our action
  3. Passing multiple parameters to our action

1. Handling parameters in our action

The first thing we are going to tackle is to get our DirectAction to handle parameters attached to our URL. Our DirectAction class has access to a WORequest object through the request() method. The WORequest contains a bunch of information about the current request, including any form values – which is how we will be passing values into our DA’s. These can be accessed through the following methods:

  • formValues()
  • formValuesForKey(String aKey)
  • formValueForKey(String aKey)

I suggest you take a look at the documentation for WORequest for more information but here is a brief summary of what they do:

  • formValues – returns an NSDictionary with all of the form values. The names are represented as the NSDictionary keys each with an NSArray containing its value(s).
  • formValuesForKey – returns an NSArray of the form value(s) that match a given name (key).
  • formValueForKey – returns an Object that is one of the value(s) that matches a given name (key).

Note: formValues() and formValuesForKey(String aKey) will return NSArray’s of values. This is because multiple form values can be associated with any given name (key). The NSArrays returned are sorted in no particular order – be warned.

Similarly, the Object returned by formValueForKey(String aKey) will be arbitrarily chosen from the NSArray of returned values – only use this method if you can be assured that there is only one value for each key (or don’t care which value you receive).

Lets extend the helloWorld example from last time to see how this works. Open your project and modify the helloWorld method in the DirectAction.class so it looks like this:

public WOActionResults helloWorldAction() {
	// Using formValues()
	NSDictionary dict = this.request().formValues();
	System.out.println("Form Values for request: " + dict);
	WOComponent page = pageWithName("Main");
	page.takeValueForKey("Hello World", "myDisplayString");
	return page;
}

Granted, this is not awe inspiring code, but it will help us to explore the next section.

2. Passing a single parameter to our action

Build the application and run it. If you are working on the project we built last time your web browser should open a new page with a single link. Click on the link to get to the Hello World page. You should see something like this in your console:

Form Values for request: {}

Which is understandable, as we haven’t supplied any values. Let’s change that. Append the following to the current URL "?=thisIsATest" so it looks like this:

http://STD_WEBO_URL/Test.woa/wa/helloWorld?=thisIsATest

The STD_WEBO_URL will look something like: "localhost:55555/cgi-bin/WebObjects/" it’s abbreviated for brevity… and to make it shorter too.

Hit return to submit the modified URL and you should see this in the console:

Form Values for request: { = ("thisIsATest"); }

Because we did not append a key-value pair to the URL, our resulting dictionary is missing the key. This is fine if we don’t need to uniquely identify our values. If we are just passing one value, we can retrieve it using formValueForKey("").

3. Passing multiple parameters to our action

Having one value is rare, in my experience, so lets look at identifying our value with a key. Appending a key to the URL is easy, the resulting URL will look like this:

http://STD_WEBO_URL/Test.woa/wa/helloWorld?aKey=thisIsATest

Submitting this URL to our application should give us this in the console:

Form Values for request: {aKey = ("thisIsATest"); }

We can explicitly retrieve this value using formValueForKey("aKey").

Finally, lets look at sending multiple key-value pairs. Simply append additional pairs (key1=value1) separated by an & symbol:

http://STD_WEBO_URL/Test.woa/wa/helloWorld?key1=value1&key2=value2

This URL should give us the following in the console:

Form Values for request: {key1 = ("value1"); key2 = ("value2"); }

Again, using formValueForKey("keyN") will allow us to get each of these values from the request.

You should now have a pretty good idea of how a simple DirectAction works, and how values are encoded into the URL. However, it would be nice if we could get the URLs without having to enter them by hand. So, next time we’ll look at generating the DirectAction URLs by using the bindings on WebObjects WODynamicElements.

Comments

7 Comments so far. Comments are closed.
  1. JR,

    What about “3. Invoking or using a Session with our action”?

  2. randy,

    I was getting all excited because I thought you were going to show how to generate the directAction URLs nicely/easily! :-(
    Still, a great article, thanks for the explanation.

  3. david,

    Randy, don’t forget to read the other articles in this series. Generating DA Url’s using any one of the WODynamicElements *is* pretty easy.

  4. Marc,

    There is something I am really not understanding here…

    First of all, why does the WODirectAction class have a session() method if it’s supposed to handle stateless requests?

    More importantly, it seems that all pages generated from direct actions do indeed have a session. I know this because the session times out. This is not the desired behavior. For example, the URL:

    http://localhost:50505/cgi-bin/WebObjects/woApp.woa/wa/login

    will invoke the method DirectAction.loginAction(), which looks like:

    public WOActionResults loginAction() {
    return pageWithName(Login.class.getName());
    }

    So far so good, but the resulting Login page has a Session. If I wait for the Session to timeout, and then click the “Login” button on that page, I wind up in Application.handleSessionRestorationErrorInContext().

    Help?

  5. david,

    Marc,

    Sessions are optional on DAs. The session() method generates or retrieves a session if you want or need one. If you explicitly don’t want a session to be generated then you need to:

    1. Not call session() within your DA.

    2. Avoid using any WOElement/WOComponent on your resulting page that will generate one (ie: Anything that tries to generate a component action – WOSubmitButton or WOHyperlink with action binding, etc.)

  6. Marc,

    Thanks, David, for that reply! I will be looking into a way of submitting form values without using a WOSubmitButton. I wish there was a way to get the best of both worlds: not being required to use a Session, but still being able to use all those useful components.