Perl Mojolicious中如何从Promise正确渲染响应?
Mojolicious中如何从Promise正确渲染响应?
运行代码时出现如下报错:
[2023-04-12] [trace] Template "example/search.html.ep" not found [2023-04-12] [trace] Nothing has been rendered, expecting delayed response
疑问
- 为什么程序会查找
example/search.html.ep模板?我并未指定它。 - 我已经在渲染内容了,Mojo为什么无法等待?
示例应用代码
1. script/my_app
#!/usr/bin/env perl use strict; use warnings; use Mojo::File qw(curfile); use lib curfile->dirname->sibling('lib')->to_string; use Mojolicious::Commands; Mojolicious::Commands->start_app('MyApp');
2. lib/MyApp.pm
package MyApp; use Mojo::Base 'Mojolicious', -signatures; sub startup ($self) { $self->secrets('s3cret'); my $r = $self->routes; $r->get('/')->to('Example#index'); $r->post('/')->to('Example#search'); } 1;
3. lib/MyApp/Controller/Example.pm
package MyApp::Controller::Example; use Mojo::Base 'Mojolicious::Controller', -signatures; sub index ($self) { return $self->render(inline => '<html><body><form method="post"><textarea name="numbers" maxlength="11">123</textarea><button type="submit">Go</button></form></body></html>'); } sub search ($self) { my $v = $self->validation; $v->required("numbers"); return $self->render(text=>"Validation Error") if $v->has_error; my $numbers = $v->param("numbers"); my @numbers = split(/\r?\n/, $numbers); Mojo::Promise ->map( {concurrency => 2}, sub { $self->ua->get_p("https://httpbin.org/delay/1?q=$_" => {'api-key'=>'shhh'}); }, @numbers) ->then( sub{ my @results = @_; my @json = map { $_->[0]->res->json } @results; return $self->render(json => \@json); }) ->catch( sub { my $err = shift; return $self->render(text => $err); }) ->wait; #return $self->render(text => "This shall result is 'Unhandled rejected promise: A response has already been rendered'"); } 1;
运行命令
morbo script/my_app
访问http://localhost:3000并提交表单,多行输入会触发并发请求。
问题解答
1. 为什么查找example/search.html.ep模板?
Mojolicious的控制器方法如果没有显式返回值,也未完成响应渲染,框架会自动根据控制器和方法名查找对应模板。你的search方法调用了->wait阻塞线程,但异步回调的渲染逻辑未被正确识别,方法执行到末尾无返回值,框架就会进入默认模板渲染流程,因此会查找example/search.html.ep。
2. 为什么Mojo无法等待渲染?
->wait会阻塞事件循环,而Mojolicious是基于事件循环的异步框架,阻塞会导致Promise的then/catch回调无法被事件循环正常处理。当wait结束后,控制器方法已执行完毕,框架认为响应未生成,所以提示"Nothing has been rendered, expecting delayed response"。
正确修改方式
移除->wait,并在Promise链前调用$self->render_later,告知框架延迟渲染响应,等待异步操作完成:
修改后的search方法代码:
sub search ($self) { my $v = $self->validation; $v->required("numbers"); return $self->render(text=>"Validation Error") if $v->has_error; my $numbers = $v->param("numbers"); my @numbers = split(/\r?\n/, $numbers); # 通知框架延迟渲染响应 $self->render_later; Mojo::Promise ->map( {concurrency => 2}, sub { $self->ua->get_p("https://httpbin.org/delay/1?q=$_" => {'api-key'=>'shhh'}); }, @numbers) ->then( sub{ my @results = @_; my @json = map { $_->[0]->res->json } @results; $self->render(json => \@json); }) ->catch( sub { my $err = shift; $self->render(text => $err); }); }
修改说明
render_later:告诉Mojolicious当前请求的响应不会立即生成,需等待后续异步操作完成后发送。- 移除
->wait:避免阻塞事件循环,让Promise回调能正常在事件循环中执行。 - 回调内无需返回值:调用
$self->render即可触发响应发送,框架会处理后续流程。
内容的提问来源于stack exchange,提问作者h q
相关产品推荐
相关产品推荐

