我需要在Apache反向代理后面放置一个Mojolicious应用程序。我无法让Mojolicious在代理后面生成工作网址。
我在Perl 5.18.1中使用Mojolicious 6.14。
这是我基于https://github.com/kraih/mojo/wiki/Apache-deployment设置的Apache反向代理配置(在路径部分中)。
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /app1 http://localhost:3000/ keepalive=On
ProxyPassReverse /app1 http://localhost:3000/
RequestHeader set X-Forwarded-HTTPS "0"
这是我的测试用例。
use 5.014;
use Mojolicious::Lite;
app->hook('before_dispatch' => sub {
my $self = shift;
if ($self->req->headers->header('X-Forwarded-Host')) {
#Proxy Path setting
my $path = shift @{$self->req->url->path->parts};
push @{$self->req->url->base->path->parts}, $path;
}
});
any '/' => sub {
my $c = shift;
$c->render('index');
};
any '/test' => sub {
my $c = shift;
$c->render('test');
};
app->start;
__DATA__
@@ index.html.ep
<!DOCTYPE html>
<html>
<head><title>Index Page</title></head>
<body>
<p>Index page</p>
<p>
%= link_to 'Go to Test Page' => '/test'
</p>
</body>
</html>
@@ test.html.ep
<!DOCTYPE html>
<html>
<head><title>Test Page</title></head>
<body>
<p>Test page</p>
<p>
%= link_to 'Return to home page' => '/'
</p>
</body>
</html>
我访问http://www.example.com/app1时可以看到索引页面,但是测试页面的链接不正确。当我预期//test
时,该链接为http://www.example.com/app1/test
。
这是测试用例的HTML输出。
<!DOCTYPE html>
<html>
<head><title>Index Page</title></head>
<body>
<p>Index page</p>
<p>
<a href="//test">Go to Test Page</a>
</p>
</body>
</html>
如何告诉Mojolicious我的应用程序的基本URL是什么,以便生成正确的链接?
答案 0 :(得分:1)
可能需要在apache config中替换http://localhost:3000/app1
上的服务器代理传递:
ProxyPass /app1 http://localhost:3000/app1 keepalive=On
ProxyPassReverse /app1 http://localhost:3000/app1
答案 1 :(得分:0)
不知何故一种解决方法,但这就是我解决这个问题的方法:
在配置文件(.conf
)中,我定义了base-url:
base_url => 'https://booking.business-apartments.wien',
这允许我写这样的模板:
%= link_to 'Payment Information' => ( config('base_url') . url_for('intern/invoice/list_payments/') );
答案 2 :(得分:0)
这是一个好问题!从这里和其他地方的一些答案来看,似乎普遍缺乏对Apache反向代理设置如何影响Mojolicious应用程序以及该钩子应该做什么的理解。
您收到的answer基本上是正确的,但它以“ 也许 [需要替换服务器代理密码...”开头,并且没有提供任何解释。试错法可能对您不起作用。如果您的钩子工作方式不同,则可能不会。
这是您的反向代理配置(删除了斜杠,请参见下文):
ProxyPass /app1 http://localhost:3000 keepalive=On
假设本地服务器的地址为http://example.com/;然后
| ProxyPass / mirror / foo / http://backend.example.com/
将导致对http://example.com/mirror/foo/bar的本地请求在内部转换为对http://backend.example.com/bar的代理请求。
现在,假设您的Apache正在localhost:80上侦听,并且您的(Morbo)应用程序服务器正在3000端口上侦听,那么Apache会收到对http://localhost/app1
的请求,并将其转发为{{1} }。 /
前缀已丢失,这就是为什么基本URL中缺少该前缀,即,所有链接中都缺少该前缀的原因。要修复应用程序生成的url,必须将此前缀添加到基本url中,这将导致我们进入钩子。
这是您的钩子函数:
app1
该钩子应该修复基本URL。如上所述,if ($self->req->headers->header('X-Forwarded-Host')) { # 1. if
my $path = shift @{$self->req->url->path->parts}; # 2. shift
push @{$self->req->url->base->path->parts}, $path; # 3. push
}
前缀需要添加到基本URL,该前缀将附加到所有生成的URL。如果您的模板之一链接到app1
,则基本网址应类似于/test
,以获取最终网址/app1
。
这是您的钩子所做的:
通过检查X-Forwarded-Host,如果请求通过反向代理,请确保仅 修改基本URL。之所以有效,是因为mod_proxy_http module (documentation)自动设置了该标头。没有该检查,您将无法直接在localhost:3000下访问应用程序服务器,所有URL都将被破坏。
实际上,我问过question on how this distinction should be made in a reliable way to fix the url prefix when using a reverse proxy without breaking requests going to the application server。不幸的是,我收到的大多数答案都是错误的。但是我相信检查X-Forwarded-Host足够好,因为它是由Apache而不是Morbo或Hypnotoad设置的。实际上,它是由反向代理设置的,而这正是您要寻找的。 p>
该/app1/test
应该从请求网址中提取前缀。
这是必要的,因为严格来说,将应用程序前缀附加到shift
指令会操纵最终请求url,因此您的应用程序会收到对ProxyPass
的请求。当然,该地址没有路由,因为您的应用程序中的路由器不知道/app1/
是该实例的前缀,而不是相对的应用程序URL。
很显然,如果您在/app1
下部署了同一应用程序的另一个副本,则将硬编码前缀/app1
添加到所有模板(有些可能会这样做)将无法正常工作。即使您没有这样做,但是如果您的提供商强迫您将/app2
前缀更改为app1
,您仍然必须更改所有链接。这就是为什么在该钩子中提取前缀,将其存储以使链接起作用(请参阅#3),然后将其从请求url中删除以使路由器满意的原因。
这是将app_one
前缀(单个路径令牌)附加到基本URL的位置。基本网址是前缀,用于模板中生成的网址。这就是将/app1
变成/test
的原因(如果请求是通过反向代理发出的。)
在您的情况下,/app1/test
变成了/test
,因为您缺少前缀。我已经在答案的最后解释了这一点。
话虽如此,您的反向代理需要操纵请求url以包括前缀以使钩子起作用:
//test
修改后,您的钩子可以正常工作
仅当设置了反向代理标头时才修改基本URL ,因为只有在使用反向代理时才需要进行修改。
所有发送到Mojolicious应用程序的请求都将带有ProxyPass /app1 http://localhost:3000/app1
前缀,例如/app1
。在此步骤中,将删除前缀以将URL转换为/app1/test
。
在第2步中删除的前缀被附加到基本URL,该URL以后将用于生成链接。
这应该解释为什么,您需要在/test
行中添加应用程序前缀。没有这种解释,其他人可能会尝试这样做而没有成功,因为他们可能具有不同的挂钩函数。
一个斜杠会破坏所有内容,并导致大多数请求失败,并显示错误404。
请注意,您的ProxyPass
行中的本地目标url(第二个参数)带有斜杠,而path参数则没有。如果不匹配,您可能会在请求网址中以双斜杠结尾,并且某些请求可能会失败。
如果第一个参数以结尾/结束,第二个参数也应该以结尾/结束,反之亦然。否则,对后端的最终请求可能会错过一些所需的斜杠,并且无法传递预期的结果。
现在,如果您删除结尾的斜杠但忘记了前缀...
ProxyPass
...生成的url仍将以两个斜杠开头:ProxyPass /app1 http://localhost:3000
= url_for '/test'
这是因为您要将//test
附加到要附加应用程序前缀的基本URL。
发生的事情是,在第2步(请参见上文)中,您提取了前缀,并假设应用程序正好在文档根目录下一级运行,即,您的前缀类似于undef
而不是{{1 }}(在这种情况下,shift / push例程必须运行两次)。但是ProxyPass指令中没有前缀,因此您的应用程序会看到类似app1
的内容,也就是说,没有任何内容可以从apps/app1
中提取。而且代码中也没有任何保障措施,因此您最终将/
推入了基本URL的parts数组。如果您随后生成一个URL,则Mojolicious会为该parts
元素添加一个额外的斜杠,这就是为什么您得到undef
的原因。零件数组如下所示:
undef
要解决此双斜杠错误,您可以为钩子添加保护措施:
//test
当然,只要您的反向代理配置中带有前缀,就应始终定义"parts" => [
undef,
"test"
],
。
可以肯定地说,这种方法是一种hack,因为它可以操纵URL。黑客在某些情况下往往会失败。在这种情况下,如果您在直接访问应用程序服务器时手动设置my $path = shift @{$self->req->url->path->parts};
if ($path) { # safeguard
push @{$self->req->url->base->path->parts}, $path;
}
,则会失败。我在my question中提到过。但是作为开发人员,您可能是唯一可以直接访问该应用程序服务器的人,因为在典型的生产环境中,防火墙只允许外部请求到反向代理。我会留在那里。
答案 3 :(得分:-1)
您是否应该尝试将Mojolicious更新为更新版本?我记得我有一个类似的问题,我用一个代码解决了它,我明确定义了代理的url并将其附加到每个请求(类似于lanti的答案)。经过一些莫名其妙的更新之后,代码就不再需要了。 而且,我认为我使用Logioniz提出的相同配置。
答案 4 :(得分:-1)
当您将应用程序安装到某个位置(而不是root /)时,很有必要继续使用它。看看Mojolicious :: Controller :: url_for
# Make path absolute
my $base_path = $base->path;
unshift @{$path->parts}, @{$base_path->parts};
$base_path->parts([])->trailing_slash(0);
您可以在此处控制生成的内容。