NAME

Kelp::Response - Format an HTTP response

SYNOPSIS

Examples of how to use this module make a lot more sense when shown inside route definitions. Note that in the below examples $self->res is an instance of Kelp::Response:

# Render simple text
sub text {
    my $self = shift;
    $self->res->text->render("It works!");
}

# Render advanced HTML
sub html {
    my $self = shift;
    $self->res->html->render("<h1>It works!</h1>");
}

# Render a mysterious JSON structure
sub json {
    my $self = shift;
    $self->res->json->render({ why => 'no' });
}

# Render the stock 404
sub missing {
    my $self = shift;
    $self->res->render_404;
}

# Render a template
sub view {
    my $self = shift;
    $self->res->template('view.tt', { name => 'Rick James' } );
}

DESCRIPTION

The PSGI specification requires that each route returns an array with status code, headers and body. Plack::Response already provides many useful methods that deal with that. This module extends Plack::Response to add the tools we need to write graceful PSGI compliant responses. Some methods return $self, which makes them easy to chain.

ATTRIBUTES

rendered

Tells if the response has been rendered. This attribute is used internally and unless you know what you're doing, we recommend that you do not use it.

partial

Sets partial response. If this attribute is set to a true value, it will cause finalize to return the HTTP status code and headers, but not the body. This is convenient if you intend to stream your content. In the following example, we set partial to 1 and use finalize to get a writer object for streaming.

sub stream {
    my $self = shift;
    return sub {
        my $responder = shift;

        # Stream JSON
        $self->res->set_code(200)->json->partial(1);

        # finalize will now return only the status code and headers
        my $writer = $responder->( $self->res->finalize );

        # Stream JSON body using the writer object
        for ( 1 .. 30 ) {
            $writer->write(qq|{"id":$_}\n|);
            sleep 1;
        }

        # Close the writer
        $writer->close;
    };
}

For more information on how to stream, see the "Delayed-Response-and-Streaming-Body" in PSGI docs.

METHODS

render

This method tries to act smart, without being a control freak. It will fill out the blanks, unless they were previously filled out by someone else. Here is what is does:

  • If the response code was not previously set, this method will set it to 200.

  • If no content-type is previously set, render will set is based on the type of the data rendered. If it's a reference, then the content-type will be set to application/json, otherwise it will be set to text/html.

    # Will set the content-type to json
    $res->render( { numbers => [ 1, 2, 3 ] } );
  • Last, the data will be encoded with the charset specified by the app.

set_content_type

Sets the content type of the response and returns $self.

# Inside a route definition
$self->res->set_content_type('image/png');

text, html, json, xml

These methods are shortcuts for set_content_type with the corresponding type. All of them set the content-type header and return $self so they can be chained.

$self->res->text->render("word");
$self->res->html->render("<p>word</p>");
$self->res->json->render({ word => \1 });

set_header

Sets response headers. This is a wrapper around "header" in Plack::Response, which returns $self to allow for chaining.

$self->res->set_header('X-Something' => 'Value')->text->render("Hello");

no_cache

A convenience method that sets several response headers instructing most browsers to not cache the response.

$self->res->no_cache->json->render({ epoch => time });

The above response will contain headers that disable caching.

set_code

Set the response code.

$self->res->set_code(401)->render("Access denied");

render_binary

Render binary files, such as images, etc. You must explicitly set the content_type before that.

use Kelp::Less;

get '/image/:name' => sub {
    my $content = Path::Tiny::path("$name.jpg")->slurp_raw;
    res->set_content_type('image/jpeg')->render_binary( $content );

    # the same, but probably more effective way (PSGI-server dependent)
    open( my $handle, "<:raw", "$name.png" )
        or die("cannot open $name: $!");
    res->set_content_type('image/png')->render_binary( $handle );
};

render_error

render_error( $code, $error )

Renders the specified return code and an error message. This sub will first look for this error template error/$code, before displaying a plain page with the error text.

$self->res->render_error(510, "Not Extended");

The above code will look for a template named views/errors/510.tt, and if not found it will render this message:

510 - Not Extended

A return code of 510 will also be set.

render_404

A convenience method that sets code 404 and returns "File Not Found".

sub some_route {
    if ( not $self->req->param('ok') ) {
        return $self->res->render_404;
    }
}

If your application's tone is overly friendly or humorous, you will want to create a custom 404 page. The best way to do this is to design your own 404.tt template and put it in the views/error folder.

render_500

render_500($optional_error)

Renders the stock "500 - Server Error" message. Designing your own 500 page is also possible. All you need to do is add file 500.tt in views/error. Keep in mind that it will only show in deployment. In any other mode, this method will display the optional error, or the stock error message.

redirect_to

Redirects the client to a named route or to a given url. In case the route is passed by name, a hash reference with the needed arguments can be passed after the route's name. As a third optional argument, you can enter the desired response code:

$self->redirect_to( '/example' );
$self->redirect_to( 'catalogue' );
$self->redirect_to( 'catalogue', { id => 243 });
$self->redirect_to( 'other', {}, 303 );

template

This method renders a template. The template should be previously configured by you and included via a module. See Kelp::Module::Template for a template module.

sub some_route {
    my $self = shift;
    $self->res->template('home.tt', { login => 'user' });
}