How to do error handling in web application written in Perl (CGI.pm)? - perl

When writing / maintaining web application written in Perl using CGI.pm, how should I handle errors (exceptions)? Webapp in question can be deployed as plain CGI app, as FastCGI app (using CGI::Fast), and as mod_perl app using ModPerl::Registry handler.
I mean here handling errors like page not found, or configuration forbids given action, or some external command used in the app failed (like e.g. cannot connect to the database).
Edit: added 2010-12-14.
The example code flow could look like this:
sub run {
...
run_request();
...
}
sub run_request {
...
$actions{$cgi->param('a')}->();
...
}
sub action_foo {
foo_body()
}
sub foo_body {
check_something()
or handle_error(some description);
}
I mean here that error might have be to be thrown / handled in some nested call, and not only in the action handler / route handler.

Pass the appropriate status code to the header method as per Creating A Standard HTTP Header

Related

Sending an unbuffered response in Plack

I'm working in a section of a Perl module that creates a large CSV response. The server runs on Plack, on which I'm far from expert.
Currently I'm using something like this to send the response:
$res->content_type('text/csv');
my $body = '';
query_data (
parameters => \%query_parameters,
callback => sub {
my $row_object = shift;
$body .= $row_object->to_csv;
},
);
$res->body($body);
return $res->finalize;
However, that query_data function is not a fast one and retrieves a lot of records. In there, I'm just concatenating each row into $body and, after all rows are processed, sending the whole response.
I don't like this for two obvious reasons: First, it takes a lot of RAM until $body is destroyed. Second, the user sees no response activity until that method has finished working and actually sends the response with $res->body($body).
I tried to find an answer to this in the documentation without finding what I need.
I also tried calling $res->body($row_object->to_csv) on my callback section, but seems like that ends up sending only the last call I made to $res->body, overriding all previous ones.
Is there a way to send a Plack response that flushes the content on each row, so the user starts receiving content in real time as the data is gathered and without having to accumulate all data into a veriable first?
Thanks in advance for any comments!
You can't use Plack::Response because that class is intended for representing a complete response, and you'll never have a complete response in memory at one time. What you're trying to do is called streaming, and PSGI supports it even if Plack::Response doesn't.
Here's how you might go about implementing it (adapted from your sample code):
my $env = shift;
if (!$env->{'psgi.streaming'}) {
# do something else...
}
# Immediately start the response and stream the content.
return sub {
my $responder = shift;
my $writer = $responder->([200, ['Content-Type' => 'text/csv']]);
query_data(
parameters => \%query_parameters,
callback => sub {
my $row_object = shift;
$writer->write($row_object->to_csv);
# TODO: Need to call $writer->close() when there is no more data.
},
);
};
Some interesting things about this code:
Instead of returning a Plack::Response object, you can return a sub. This subroutine will be called some time later to get the actual response. PSGI supports this to allow for so-called "delayed" responses.
The subroutine we return gets an argument that is a coderef (in this case, $responder) that should be called and passed the real response. If the real response does not include the "body" (i.e. what is normally the 3rd element of the arrayref), then $responder will return an object that we can write the body to. PSGI supports this to allow for streaming responses.
The $writer object has two methods, write and close which both do exactly as their names suggest. Don't forget to call the close method to complete the response; the above code doesn't show this because how it should be called is dependent on how query_data and your other code works.
Most servers support streaming like this. You can check $env->{'psgi.streaming'} to be sure that yours does.
Plack is middleware. Are you using a web application framework on top of it, like Mojolicious or Dancer2, or something like Apache or Starman server below it? That would affect how the buffering works.
The link above shows an example by Plack's author:
https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream-sync.psgi
Or you can do it easily by using Dancer2 on top of Plack and Starman or Apache:
https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Manual.pod#Delayed-responses-Async-Streaming
Regards, Peter
Some reading material for you :)
https://metacpan.org/pod/PSGI#Delayed-Response-and-Streaming-Body
https://metacpan.org/pod/Plack::Middleware::BufferedStreaming
https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream.psgi
https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/nonblock-hello.psgi
So copy/paste/adapt and report back please

Dustjs helper for frontend

So I am using Dustjs on Sailsjs for my project.
I am rendering the first page on the server, and then use the same template client-side.
Problem: My template contains a global sails service which doubles as a dustjs helper:
{#sails.services.globalutils.hyphenator str=title/}
But, on the client-side, I am unable to uyse this service. How can I export this service to the client without going for a JS solution? Can it be bundled with the dustjs template ?
A {#section} signals that Dust should look in the context provided to dust.render(). So as long as the hyphenator function doesn't have server-side dependencies, you can just bring it along in your client-side context. Sails services are just Javascript modules in the api/services folder, so try requireing the relevant module, grabbing its hyphenator property, and passing that along to the client to use in the client's render call.
{
"sails": {
"services": {
"globalutils": {
"hyphenator": function(chunk, context, bodies, params) {
// do some hyphenation
}
}
}
}
}
Of course, if hyphenator has logic that relies on the server, you can't just move it to the client. You could expose a server-side API that your script calls, and couple that with chunk.map to create an asynchronous Dust block.

Perl Dancer after hook

Is there a way in Dancer to execute a code after every request?
I tried with an after hook but it seems that it doesn't execute after a file request...
There is a hook called 'after_file_render' which is executed a decent number of times after each request but I am not sure what is its purpose. Is it always called after every request?
The after_file_render hook is run after each successful request for a static file (e.g., a CSS file or an image), while the after hook runs after an action is performed by a route handler.
If you want to run the same code for both after and after_file_render, you can put it in a subroutine and assign it to the two hooks using a reference, e.g.:
sub foo {
...
}
hook after_file_render => \&foo;
hook after => \&foo;

async_sinatra requests crash silently with em-http. How do I fix this?

I have the following code:
require 'rubygems'
require 'eventmachine'
require 'em-http'
require 'sinatra/base'
require 'sinatra/async'
class Api < Sinatra::Base
register Sinatra::Async
aget '/1' do
EventMachine.run {
http = EventMachine::HttpRequest.new( "http://www.google.com").get(:timeout => 5)
http.callback { puts "h2" ;ret_val = http.response; EventMachine.stop}
http.errback {puts "was h2ere1" ;ret_val = nil; EventMachine.stop}
}
body "done processing 1"
end
aget '/2' do
body "done processing 2"
end
end
When I issue the following, it works well:
curl http://localhost:3000/2
But, when I issue the following request, it prints "h2" and the application silently quits:
curl http://localhost:3000/1
Any help will be appreciated. Thanks!
If your web server (Eg. thin) is based on EventMachine, then the EventMachine.stop line will actually stop the webserver as well as the EventMachine instance created by EventMachine.run.
I can't find a way to stop nested EventMachines like this. My advice - use Weary or another non-blocking HTTP request library.
Sinatra::Async provides it's own body helper that needs to be called from within the EventMachine loop. Also worth noting: if you're running Sinatra through Thin, you shouldn't call EM.run explicitly, as Sinatra is already operating within an EventMachine loop.

What is the best way to log errors in Zend Framework 1?

We built an app in Zend Framework (v1) and have not worked a lot in setting up error reporting and logging. Is there any way we could get some level or error reporting without too much change in the code? Is there a ErrorHandler plugin available?
The basic requirement is to log errors that happens within the controller, missing controllers, malformed URLs, etc.
I also want to be able to log errors within my controllers. Will using error controller here, help me identify and log errors within my controllers? How best to do this with minimal changes?
I would use Zend_Log and use the following strategy.
If you are using Zend_Application in your app, there is a resource for logging. You can read more about the resource here
My advice would be to choose between writing to a db or log file stream. Write your log to a db if you plan on having some sort of web interface to it, if not a flat file will do just fine.
You can setup the logging to a file with this simple example
resources.log.stream.writerName = "Stream"
resources.log.stream.writerParams.stream = APPLICATION_PATH "/../data/logs/application.log"
resources.log.stream.writerParams.mode = "a"
resources.log.stream.filterName = "Priority"
resources.log.stream.filterParams.priority = 4
Also, I would suggest sending Critical errors to an email account that is checked regularly by your development team. The company I work for sends them to errors#companyname.com and that forwards to all of the developers from production sites.
From what I understand, you can't setup a Mail writer via a factory, so the resource won't do you any good, but you can probably set it up in your ErrorController or Bootstrap.
$mail = new Zend_Mail();
$mail->setFrom('errors#example.org')
->addTo('project_developers#example.org');
$writer = new Zend_Log_Writer_Mail($mail);
// Set subject text for use; summary of number of errors is appended to the
// subject line before sending the message.
$writer->setSubjectPrependText('Errors with script foo.php');
// Only email warning level entries and higher.
$writer->addFilter(Zend_Log::WARN);
$log = new Zend_Log();
$log->addWriter($writer);
// Something bad happened!
$log->error('unable to connect to database');
// On writer shutdown, Zend_Mail::send() is triggered to send an email with
// all log entries at or above the Zend_Log filter level.
You will need to do a little work to the above example but the optimal solution would be to grab the log resource in your bootstrap file, and add the email writer to it, instead of creating a second log instance.
You can use Zend_Controller_Plugin_ErrorHandler . As you can see on the documentation page there is an example that checks for missing controller/action and shows you how to set the appropriate headers.
You can then use Zend_Log to log your error messages to disk/db/mail.