Hi!
Very nice point. Could you please create a JIRA issue?
On Tue, 29 Sep 2015 13:55:10 -0300, Cezary Biernacki <cezary...@gmail.com>
wrote:
Hi,
Some introduction: My users started complaining about error message
"Forms
require that the request method be POST and that the t:formdata query
parameter have values.". I understand why it was raised: Tapestry 5's
Form
component always used to complain when submit was invoked as GET
operations
(e.g. somebody typed "http://host/mypage.myform" URL directly in the
address bar) . Pre-5.4 it was not big issue, because it was rather
unusual
to users to accidentally invoke submit URL as GET operations, as Tapestry
always used Redirect-After-POST approach, and submit URLs typically were
not saved by browsers in the history. However Tapestry 5.4 does not
redirect after POST in case of server-side validation errors on submitted
forms (e.g. password checking). IIRC, this change was introduced to avoid
unnecessary creation of server side sessions.
Again, I understand that. But now submit URLs are often exposed address
bar
(e.g. every time a user makes mistake entering password), stored in the
browser's history, and - most importantly - suggested by browsers when
users start typing the application address. I believe such behaviour
leads
to bad usability and leaves unpleasant impression of Tapestry's
application. As far as I can tell there is no simple work around to avoid
the exception (beside totally rewriting Form component). Only way I
found
that works reasonably well is to suppress the exception in
RequestExceptionHandler and perform some other action - in my case:
redirect to the page - see code below.
However detecting this particular exception is tricky as it is just
generic
RuntimeException (why?) and the message content is localised
(see org.apache.tapestry5.corelib.components.Form#executeStoredActions -
around line 700
https://github.com/apache/tapestry-5/blob/master/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java#L701),
so detecting is quite fragile.
My questions:
- do you know any better way to deal with this problem?
- do you see any potential issues with my solution?
And appeal to Tapestry's creators: please introduce a more specific
exception class and/or allow application developers to supply their own
strategy dealing with such situation.
Best regards,
Cezary
// skipped header (imports etc.)...
public class FormPostProblemWorkaroundExceptionHandler implements
RequestExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger
(FormPostProblemWorkaroundExceptionHandler.class);
private final RequestExceptionHandler delegate;
private final RequestGlobals requestGlobals;
private final BaseURLSource baseURLSource;
private final PageRenderLinkSource pageRenderLinkSource;
public FormPostProblemWorkaroundExceptionHandler(
final RequestExceptionHandler delegate,
final RequestGlobals requestGlobals,
final BaseURLSource baseURLSource,
final PageRenderLinkSource pageRenderLinkSource
) {
this.delegate = delegate;
this.requestGlobals = requestGlobals;
this.baseURLSource = baseURLSource;
this.pageRenderLinkSource = pageRenderLinkSource;
}
@Override
public void handleRequestException(final Throwable exception) throws
IOException
{
if (isExceptionAboutFormExpectingPostMethod(exception)) {
redirect(exception);
return;
}
delegate.handleRequestException(exception);
}
private boolean isExceptionAboutFormExpectingPostMethod(final Throwable
exception) {
// unfortunately, original exception is just RuntimeException and its
message is localised
// (see Tapestry message ID core-invalid-form-request), only constant
information is reference to t:formdata argument
// see
org.apache.tapestry5.corelib.components.Form.executeStoredActions()
return exception instanceof OperationException
&& exception.getCause() instanceof ComponentEventException
&& exception.getMessage().contains(Form.FORM_DATA);
}
private void redirect(final Throwable exception) throws IOException {
final Request request = requestGlobals.getRequest();
final String failurePage = (request.getAttribute(InternalConstants.
ACTIVE_PAGE_LOADED) == null)
? null
: requestGlobals.getActivePageName();
final Response response = requestGlobals.getResponse();
if (failurePage == null) {
final String rootURL = baseURLSource.getBaseURL(request.isSecure());
LOGGER.info("Redirecting to root URL {} after '{}'", rootURL,
exception.getMessage());
response.sendRedirect(rootURL);
return;
}
final Link link = pageRenderLinkSource.createPageRenderLink(failurePage);
LOGGER.info("Redirecting to page {} after '{}'", link,
exception.getMessage());
response.sendRedirect(link);
}
}
// add this method in your AppModule class
@Decorate(serviceInterface = RequestExceptionHandler.class)
public static RequestExceptionHandler
installWorkaroundForFormPOSTProblem(
@Nonnull final RequestExceptionHandler delegate,
@Nonnull final RequestGlobals requestGlobals,
@Nonnull final BaseURLSource baseURLSource,
@Nonnull final PageRenderLinkSource pageRenderLinkSource
) {
return new FormPostProblemWorkaroundExceptionHandler(delegate,
requestGlobals, baseURLSource, pageRenderLinkSource);
}
--
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org