You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Mojolicious中为403页面渲染自定义模板?

如何让Mojolicious路由条件触发的403使用自定义模板?

我编写了一个用于检查当前用户是否有权访问URL的函数,并通过add_condition将其附加到路由,具体代码如下:

use Mojolicious::Lite;

# Adding a custom condition
app->routes->add_condition(auth => sub {
    my ($route, $c, $captures, $num) = @_;

    $c->res->code(403);
    $c->rendered(403);
    # Here, you could define your condition. For example, check if a parameter is set.
    return 0;  # true/false depending on parameter
});

my $r = app->routes;
$r->get('/block')->requires(auth => 1)->to(cb => sub {
  my $c = shift;
  $c->render(template => 'default');
});

# Route for home
get '/' => sub {
    my $c = shift;
    $c->render(template => 'main');
};

# Default template
app->start;

__DATA__

@@ 403.html.ep
<!DOCTYPE html>
<html>
<head><title>Default Template</title></head>
<body>
<h1>Welcome to the Default Template</h1>
<p>This is the default template.</p>
</body>
</html>

@@ main.html.ep
<!DOCTYPE html>
<html>
<head><title>Main page</title></head>
<body>
<h1>Welcome to the main page</h1>
<p>The main page is here.</p>
</body>
</html>

当前访问http://127.0.0.1:3000/block会返回带有403状态码的空白页面,而访问http://127.0.0.1:3000/可正常渲染主页。请问我该如何配置才能为403页面使用自定义模板?


问题原因

你在路由条件里调用了$c->rendered(403),这个方法仅标记响应流程结束,但不会自动渲染对应状态码的模板,所以返回了空白页面。

解决方法

方法一:在路由条件中直接渲染403模板

修改auth条件的代码,去掉rendered(403),改用render方法指定模板和状态码,同时保留你的权限检查逻辑:

use Mojolicious::Lite;

# 修改后的自定义条件
app->routes->add_condition(auth => sub {
    my ($route, $c, $captures, $num) = @_;

    # 这里替换为你的实际权限判断逻辑,比如检查session、请求参数等
    my $has_permission = 0; # 示例:当前无权限

    if (!$has_permission) {
        # 直接渲染自定义403模板,并设置状态码
        $c->render(template => '403', status => 403);
        return 0;
    }

    return 1;
});

my $r = app->routes;
$r->get('/block')->requires(auth => 1)->to(cb => sub {
  my $c = shift;
  $c->render(template => 'default');
});

# Route for home
get '/' => sub {
    my $c = shift;
    $c->render(template => 'main');
};

app->start;

__DATA__

@@ 403.html.ep
<!DOCTYPE html>
<html>
<head><title>403 访问被禁止</title></head>
<body>
<h1>403 Forbidden</h1>
<p>你没有权限访问此页面,请检查你的权限设置。</p>
</body>
</html>

@@ main.html.ep
<!DOCTYPE html>
<html>
<head><title>Main page</title></head>
<body>
<h1>Welcome to the main page</h1>
<p>The main page is here.</p>
</body>
</html>

方法二:全局统一处理403错误(推荐)

如果希望所有触发403状态码的请求都自动使用自定义模板,可以添加全局钩子,避免在每个路由条件里重复写渲染逻辑:

use Mojolicious::Lite;

# 添加全局错误处理钩子
app->hook(after_render => sub {
    my ($c, $output, $format) = @_;
    # 当响应状态码为403且输出为空时,渲染自定义模板
    if ($c->res->code == 403 && !$$output) {
        $c->render(template => '403', status => 403);
    }
});

# 路由条件保持简洁,仅设置状态码和返回权限判断结果
app->routes->add_condition(auth => sub {
    my ($route, $c, $captures, $num) = @_;

    # 权限判断逻辑
    my $has_permission = 0;
    if (!$has_permission) {
        $c->res->code(403);
        return 0;
    }

    return 1;
});

my $r = app->routes;
$r->get('/block')->requires(auth => 1)->to(cb => sub {
  my $c = shift;
  $c->render(template => 'default');
});

get '/' => sub {
    my $c = shift;
    $c->render(template => 'main');
};

app->start;

__DATA__

@@ 403.html.ep
<!DOCTYPE html>
<html>
<head><title>403 访问被禁止</title></head>
<body>
<h1>403 Forbidden</h1>
<p>你没有权限访问此页面,请检查你的权限设置。</p>
</body>
</html>

@@ main.html.ep
<!DOCTYPE html>
<html>
<head><title>Main page</title></head>
<body>
<h1>Welcome to the main page</h1>
<p>The main page is here.</p>
</body>
</html>

关键说明

  • $c->render()方法会自动完成模板渲染、状态码设置和响应发送,无需再调用rendered();
  • 方法一适合对特定路由的403页面做个性化定制;
  • 方法二更适合全局统一处理所有403错误,减少重复代码。

内容的提问来源于stack exchange,提问作者user4035

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:09:55