Hi Simon,
Simon Flack wrote:
> This isn't the solution, but you may find that you get better behaviour
> if you enable 'debug'.
IIRC, the only case where that makes a difference is if there's a
perfectly formed URL leading to an action that successfully creates
objects but the view fails because of a missing template.
> You can set a custom 404 page with Maypole 2.05 and TT2:
>
> __PACKAGE__->config->view_options({
> DEFAULT => 'error404.tt',
> });
Thanks for this; it's definitely an improvement over 500 Server Error.
Here's a starting point for error404.tt:
[%-
# This is the error404 template.
# First ignore missing sub-templates
UNLESS template.name == component.name ; RETURN ; END ;
INCLUDE header ;
-%]
<p>
Sorry, we couldn't find what you requested ([% request.path %]).
</p>
[% INCLUDE footer %]
That's not a complete solution though. It catches mistakes in table or
action names but as you say it returns a 200 status. That's OK if
there's a human looking at the response but not so good if there's a
program involved. So I think it would be better not to use the TT
DEFAULT but instead for Maypole::View::Base->error to set the
appropriate status header and then process an error404 template.
There's also another issue. While testing I noticed that if you give a
valid table and action (e.g. view) but an invalid object id, Maypole
shows the resulting page (an empty view page for example). If you press
edit on that view page it goes to a garbled URL (.../beer.cgi//edit/).
What happens is Class::DBI returns undef from the retrieve, and
M::M::CDBI->fetch_objects passes undef back. M::M::Base->process
packages the undef in a list reference! and invokes the action. I'm not
sure what should happen here. At one extreme you could argue that this
is OK and each and every action/template ought to test for that case for
ultimate flexibility. At the other extreme, I think process() could just
unset the template and return so eventually M::V::Base->error would drop
into the 404 case. Or you could add hooks in various places etc etc.
> And you can set a 403 template in your MyDriver->exception. I expect
> that Maypole will respond with a 200 OK status in both these cases.
Errm isn't exception() only called in the specific case of authenticate,
model-process or view-process internal failures (which perhaps *should*
be 500s) as opposed to the case of them returning an ERROR or DECLINED
status?
Incidentally, do you know why DECLINED has the same value as ERROR?
> I think this is mostly a CGI issue. I have some ideas for how we may
> solve this but the handler() is already quite complicated, so I want to
> work on some use cases first.
I'd agree with that :) I'd got as far in my investigation as making
some notes. I've included them below; they're just my opinion.
Cheers, Dave
=head1 Error Handling
=head2 Design principles
=over
=item Custom Error Handling
Maypole should provide a way for the developer to override all error
handling, but should provide a sensible default in all cases.
=item Default Error Response
Maypole should always send an error page to the user's browser, based on
a factory error template, or custom one if present. It should send
hard-coded default text only in the specific case of not being able to
open or process that template.
=item Error Logging
Maypole should write an Apache log entry whenever it sends an error
response to the user. Where the error is one that is anticipated in the
code, or simply reporting invalid user input, the log entry should be a
single line C<warn> unless C<debug> has been turned on. Unanticipated
errors (generally those caught by C<eval>), and all errors when
debugging is enabled, should result in a C<carp> stack trace.
=item Access Control Errors
Access control errors are those where C<call_authenticate> returns any
value other than OK, normally as a result of a global or per-class
C<authenticate> routine returning such a value.
By default, access control errors should not allow the user to see any
information they did not supply.
Access control errors should be dealt with separately, such that if the
developer overrides the default error handlers, she does not have to
include special access control error handling code. There should be a
separate override mechanism for access control errors.
=back
=head2 Implementation
What happens currently is:
Errors are detected in various places
and handled in handler_guts as follows:
=over
=item eval failure
The main C<process> methods in the model and view classes are run
inside C<eval>s. If any unanticipated error is returned (i.e. if C<$@>
is true) then C<handler_guts> calls a method C<call_exception>, which
works in a similar but not identical way to C<call_authenticate>.
C<call_exception> checks whether the model class has an C<exception>
method and if so calls that with the error text as an argument.
If the model's C<exception> method returns C<OK> (0) - that is, if it
decides this is not really an error - then C<call_exception> returns
C<OK>.
If the model's C<exception> method doesn't return C<OK> or doesn't exist
at all, then C<call_exception> calls the C<exception> method in the
application class (e.g. C<BeerDB>).
The C<Maypole> class provides an implementation of C<exception>
that always returns C<ERROR>.
The application class inherits this unless it overrides it.
When these calls return to C<handler_guts> it continues if they returned
C<OK>. If not it returns C<ERROR> unless debugging is enabled, in which
case it calls the view class's C<error> method and returns whatever it
returns.
The standard C<Maypole::View::Base-E<gt>error> method checks to see if
everything was alright except for a missing template file by checking
whether there is anything in C<$r-E<gt>objects>. If so it completes a
hardcoded HTML warning page and sets that as the value of
C<$r-E<gt>error>.
Otherwise it just returns C<DECLINED> (-1, same as C<ERROR>).
Quite why it only returns valid output (in the shape of hardcoded HTML)
only in the case of a missing template, and quite why it is only called
if debugging is enabled, is not clear. Neither is it clear why
C<DECLINED> has the same value as C<ERROR>.
=item non-zero status
If the model or view C<process> methods detect an error, they don't set
C<$@>, they just return a non-zero status. Specifically at present they
return either C<ERROR> or C<DECLINED>, B<both> of which are defined as -1!
In these cases handler_guts just returns the status to the handler,
without setting any output.
=back
So as far as I can see the end result is that if you're not running in
debugging mode, the users always see HTTP 500 Server Error.
If you are running in debug mode they always see that unless the fault
happens to be a missing template.
None of which makes sense to me.
=cut
-- No virus found in this outgoing message. Checked by AVG Anti-Virus. Version: 7.0.298 / Virus Database: 265.6.6 - Release Date: 28/12/04_______________________________________________ maypole-dev mailing list maypole-dev at lists.netthink.co.uk http://lists.netthink.co.uk/listinfo/maypole-dev
This archive was generated by hypermail 2.1.3 : Thu Feb 24 2005 - 22:25:57 GMT