Wednesday, October 9, 2013

Sending Errors From Dancer

This is my first blog post on the work I've done on WeBWorK.  This one's a little more techie, but you gotta start somewhere.

Over the past few weeks I rewrote the backend of WeBWorK using the Dancer framework. I never completely understood the old webservice for WebWorK, but Dancer replicates much of it in a more flexible manner.  Instead of going through all the details, here's how I recently decided to have Dancer send errors in a more flexible manner.

As an example, suppose you need to delete a particular problem set. The code for this is:


del '/courses/:course_id/sets/:set_id' => sub {

    if (!vars->{db}->existsGlobalSet(param('set_id'))){
        send_error("The set " . param('set_id'). " doesn't exist for course " . param("course_id"),404);
    }

    my $setToDelete = vars->{db}->getGlobalSet(param('set_id'));

    if(vars->{db}->deleteGlobalSet(param('set_id'))){
        return convertObjectToHash($setToDelete);
    } else {
        send_error("There was an error while trying to delete set " . param('set_id'),424);
    }

};

The first line is the route which says to make a delete request and the parameters are the course_id and the set_id. Before, I would send back a javascript object with an error field and then try to detect it.  Now, I'm sending a http error with certain code.  The first (a 404) is a resource not found and the second (424) is a custom one.

Now a client that connects to the webservice can know that there is an error and the type error.  It's the job of the client to interpret the error and react.

The other nice thing about this is authentication errors.  I want (nearly) every route to be checked for authentication and permission. I've discovered that in Dancer, I can build the route:


any ['get','put','post','del'] => '/**' => sub {

 checkRoutePermissions();
 authenticate();

 pass;
};

which will be called for all routes (although I admit, I'm sure why this is called first) which I will check authentication and routePermissions. If the user isn't authenticated, then a 401 error will be sent.  The client can then listen for any error and if it detects a 401, then can ask for reauthentication.

I tried this earlier with the before hook which is called before anything in Dancer, but evidently the send_error function doesn't work the before hook.

The last line in the route above then passes to the next matching route.  This is away that you can chain routes together.

You can test this new code out by cloning the repository at https://github.com/pstaabp/webwork2 and make sure you then checkout the feature/new-ui-tools branch.