Question

I'm trying to create a Catalyst app that is pretty flexible for my designers. By this I mean that if they need to add a new page with static content on it, I don't have to go into Catalyst and add that action before they can create a template for the page. I have it so that if they add any template named name.html, then the Catalyst app can display http://www.domain.com/name.html even without a name action. Currently I have a subroutine in the Root controller that looks like this:

sub any_template :Regex('(.+)\.html$') { 
    my ($self, $c) = @_; 
    $c->stash(
        template => $c->action . '.html',
    );
}

and this makes it so that any request ending with '.html' will look for a template by that name. However, if a url is requested for a template that does not exists, Template Toolkit throws an error saying that it could not render the template, which makes sense since it does not exist. However, I would like to instead be able to display my 404 not found page if a template does not exist. Is it possible to do this? Or does anyone have a better way to make it to where you can have template pages without having to add an empty action to your Catalyst app? I know that it could be possible to use perl's -e file test to see if the template exists on the filesystem, but are there any better ways to do it? Thanks!

UPDATE

This is what my current wrapper looks like:

[% 
    debug("Applying HTML page layout wrappers to $template.name\n");
    content WRAPPER "$host/site/html" + "$host/site/layout";
-%]

This was mostly generated by TTSite. Is there anyway I could do something similar to what RET was suggesting, where I could use a try catch block to use a 404 page if this fails?

UPDATE

I've tried updating my wrapper template to this:

[%- 
    TRY;
        content WRAPPER "$host/site/html" + "$host/site/layout"; 
    CATCH file;
        "File Error! $error.info";
    CATCH;
        "Error: $error.info!";
    END;
-%]

And it will say that there is a file error when it cannot find the page:

Couldn't render template test.html: file error - test.html: not found

However, it is not going to either of my catch blocks. Should I be putting it somewhere else?

UPDATE

The Template Toolkit documentations says:

Note that the DEFAULT option (disabled by default) allows you to specify a default 
file to be used any time a template file can't be found. This will prevent file 
exceptions from ever being raised when a non-existant file is requested (unless,
of course, the DEFAULT file your specify doesn't exist). Errors encountered once 
the file has been found (i.e. read error, parse error) will be raised as file 
exceptions as per usual.

This is exactly what I am looking for, but there are no examples of how to use this. Does anyone know how to use the DEFAULT option?

Was it helpful?

Solution 3

So I ended up figuring out a solution that allowed me to do what I wanted. Instead of using the default RenderView, I created my own:

#old end action
#sub end : ActionClass('RenderView') {}

sub end : Private { 
    my ($self, $c) = @_; 

    return if $c->res->body ne '';  

    my $output;
    eval { $output = $c->view($c->stash->{current_view})->render($c,$c->stash->{template})};

    unless($@) { 
        $c->res->body($output);
    }   
    else {
        $c->res->status(404);
        $c->stash->{template} = 'errors/404.html';
        $c->forward($c->view('NO_HTML'));
    }   
}

It is possibly a little bit hacky, but it does what I want and allows me to display a 404 page along with a response of 404 whenever a page is not found, and this allows me to keep the flexibility of not having to define an action for every template that I create.

OTHER TIPS

You probably don't need to use Catalyst at all for your static content. Usually, static content like images, CSS, Javascript is served directly by your web server (nginx or APache). You could do the same with your HTML static files.

If you really want to mix static and dynamic content, look at Catalyst::Plugin::Static::Simple]1 and the $c->serve_static_file($file_path) function.

I've upvoted Julien's answer, because he's exactly right - this is the best way to handle truly static resources. If it's just HTML, get them to put it in the static area and set up alias or location records as required for your web-server.

However, if your designers are producing templates that need to be put inside a WRAPPER or need some dynamic handling, you will quickly hit some limits with that approach.

Rather than process the template directly like that, you could do something like this:

=== Root.pm ===

sub any_template {
    ...
    $c->stash(template => 'static.tt');
}

=== static.tt ===

[%- WRAPPER foo.tt -%]
[%- SET tmpl = c.action;
    TRY;
        # use either PROCESS if a template, or INCLUDE if it's HTML
        PROCESS tmpl _ ".tt";
        # INCLUDE tmpl _ ".html"; # or this
    CATCH;
        PROCESS "404.tt";
    END -%]
[%- END -%]

So that the file-not-found is gracefully handled. Using a 404.tt template means your wrapper's header/footer etc is still presented.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top