SOLVED (*really*, this time!): Re: returning PDF via appendToResponse
SOLVED (*really*, this time!): Re: returning PDF via appendToResponse
- Subject: SOLVED (*really*, this time!): Re: returning PDF via appendToResponse
- From: Patrick Robinson <email@hidden>
- Date: Fri, 14 Dec 2007 14:28:07 -0500
It turns out that the problem I was having, and the reason my
"solution" worked, has nothing to do with PDF files.
It has to do with the request-response loop.
By default, WOApplication.isPageRefreshOnBacktrackEnabled() is true.
One implication of this is that (to paraphrase the API documentation
for setIsPageRefreshOnBacktrackEnabled) when the app receives a
request that it has already received, the first two phases of the req-
res loop (takeValues and invokeAction) do not occur. Only the
third phase, appendToResponse, is called; that is, the response page
for the context will have appendToResponse called on it, to
regenerate its response.
In my action method, I was setting a variable and returning null.
The variable that was set was being handled in my appendToResponse,
causing it to replace the "normal" component-based woresponse with a
pdf file that's returned with a content-disposition of "attachment".
This means that the page in my web browser is not refreshed. When I
click the link that's bound to my action method a second time, WO
recognizes that this is a duplicate of a previously handled request,
and so it does not call my action method, but only my
appendToResponse. But in my awake(), I clear the variable that my
action method sets -- the one my appendToResponse uses to determine
its behavior.
When I changed things so that my action method instead returned a
second component -- one with empty html and wod, and which just has
an appendToResponse that returns the file attachment, the situation
became somewhat different. The first time I click the link bound to
my action method, the action method creates (via pageWithName) an
instance of the second component, and this component returns the pdf
file in its appendToResponse. As before, my web browser's view of
things is not refreshed. The second time I click the link, WO
recognizes the request as a duplicate, does NOT re-invoke my action
method, but calls appendToResponse on the previously created response
page (the one I created in my action method via pageWithName). So
the file is returned a second time, as expected.
As Steve Quirk pointed out to me, I could also have simply created a
new response in my action method, set its content and headers, and
returned it directly from my action method. In this case, repeatedly
clicking a link bound to the action method will, as before,
repeatedly send the identical request to WO. But the behavior is
again different in this case. Normally, a component's
appendToResponse is called from its generateResponse method. But if
I return a WOResponse (of my own creation) from my action method
(instead of returning a WOComponent), then generateResponse is not
called, and so appendToResponse is not called. My action method will
be called repeatedly on subsequent identical requests. If a
WOComponent is returned by an action method (which implements the
WOActionResults interface), then that WOComponent will be stored in
the context, and can have generateResponse/appendToResponse called on
it again if an identical request is received. But if a WOResponse is
returned by the action method, this doesn't occur -- , and so
subsequent identical requests will result in the action method's
being reinvoked.
Interesting, no?
- Patrick
On Dec 13, 2007, at 5:34 PM, Patrick Robinson wrote:
Well, it's always fun to reply to one's own emails.
First, the solution to (1) is to have the action method return a
different page, e.g.
public WOComponent downloadFile() {
WOComponent fileComponent = pageWithName("FileDownloadComponent");
fileComponent.setSelectedThing(aThing);
return fileComponent;
}
Then in FileDownloadComponent.appendToResponse(), you return the PDF:
public void appendToResponse( ... ) {
res.setHeader(mimeType, "content-type");
res.setHeader("attachment; filename=" + filename, "content-
disposition");
res.setContent(selectedThing.data());
res.disableClientCaching();
res.removeHeadersForKey("Cache-Control");
res.removeHeadersForKey("pragma");
}
FileDownloadComponent's .html and .wod files are empty, so it
doesn't seem to matter whether or not you call
super.appendToResponse().
The result of doing it this way is that when I log the WOResponse
in Application.dispatchRequest(), all I see is the PDF file
response that I set, above. I can click the link to invoke the
action method repeatedly, but I only ever see the one WOResponse, now.
The solution to (2) is simply to change the content-disposition
from "attachment" to "inline". That is:
res.setHeader("inline; filename=" + filename, "content-
disposition");
- Patrick
On Dec 13, 2007, at 3:18 PM, Patrick Robinson wrote:
I've seen lots of examples for returning a PDF by fiddling with
the WOResponse in a component's appendToResponse() method. Code
like this:
public void appendToResponse(WOResponse res, WOContext con) {
super.appendToResponse(res, con);
if (someCondition) {
res.setContent(pdfFile);
res.setHeader("application/pdf", "Content-Type");
res.setHeader("attachment; filename=MyFile.pdf",
"Content-disposition");
res.disableClientCaching();
res.removeHeadersForKey("Cache-Control");
res.removeHeadersForKey("pragma");
}
}
We've made use of this kind of thing by having an action method
that sets "someCondition", as well as the details of what pdfFile
to download:
public void awake() {
someCondition = false;
}
public WOComponent downloadFile() {
someCondition = true;
pdfFile = nsDataForSelectedFile(selectedFile);
}
I have two problems:
(1) I was getting the PDF downloaded only every other time I
clicked the link bound to the downloadFile() action method.
Because I was curious, I implemented Application.dispatchRequest
(), and discovered that the first time I clicked the link, I'd get
a PDF file in the WOResponse. The next time I clicked it, the
WOResponse would contain the HTML of the page where the link
lived! Then, the next time, I'd get the pdf again. Why is this?
(2) I'd like to get the pdf to open in the browser, in the Acrobat
plugin (if there is one), rather than downloading it to the
downloaded file dir. I've read things suggesting that:
- you just need to change the content-disposition from
"attachment" to "inline".
- you may also need to specify "; name=MyFile.pdf" after the
"application/pdf" content-type.
- you may or may not need to specify the content-length.
- you shouldn't be calling super.appendToResponse(). I don't
understand how this would make a difference, as I'm replacing the
response's content, anyway.
But none of these things seems to work. Just changing the content-
disposition to inline has the effect of making the browser window
go black for a sec, then back to the HTML page (making me think
maybe I was on the right track).
At this point, I discovered problem (1), and wondered if the way
I'm mangling the WOResponse has something to do with it.
--
Patrick Robinson
AHNR Info Technology, Virginia Tech
email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden