Perl Catalyst - 从AJAX POST请求获取JSON数据

5
我将尝试获取通过AJAX POST发送到服务器的JSON数据,当我的Catalyst应用程序上的主屏幕页面上的按钮被点击时:
发送JSON数据到服务器的AJAX POST:
$("#saveCanvasStates").click(function () {
// button to save canvas states to a database

// Serialize the states array
var JsonStringForTransport = JSON.stringify({stateForUserNumber7: states});

// POST the JSON to the server
var thePost = $.ajax({
    url: 'homescreen',
    type: 'POST',
    dataType: 'json',
    data: JsonStringForTransport,
    contentType: 'application/json; charset=utf-8'
});

我还有一个Catalyst控制器,用于homescreen页面,发送AJAX POST的按钮位于此页面上:

Catalyst控制器:

package MyProject::Controller::Homescreen;

use strict;
use warnings;
use parent 'Catalyst::Controller';
use Data::Dumper;

__PACKAGE__->config->{namespace} = '';

sub homescreen :Path('/homescreen') :Args(0)  {

        my ( $self, $c ) = @_;

        $c->stash({title => 'Home Screen',
                   pagetype => 'html',
                   template => 'homescreen.html'
                 });


#here is where I think I need to obtain the JSON data from the AJAX POST request 
#and save it to my database

}

1;

一旦我以可操作的形式得到这些JSON数据,就会将其保存到Postgres数据库中。从查看Catalyst::Request的CPAN文档来看,我的理解是在处理请求方面所需参考的内容,是否可以使用以下内容来处理AJAX POST数据?: $c->$req->body_data $c->$req->body_parameters $c->$req->body_params
但我不确定最佳方法是将数据放入可插入数据库的表格中,也不确定应该优先使用哪种方法。我找到的文档很少能帮到我。
更新(与RET的答案相关):我肯定有要显示的body_data,因为当我执行以下操作时:print Dumper($c->req->body_data);
我在开发服务器日志中得到以下输出:
$VAR1 = {
          'stateForUserNumber7' => [
                                     {
                                       'width' => 102,
                                       'offsetY' => 56,
                                       'x' => 11,
                                       'height' => 102,
                                       'image' => {},
                                       'y' => 14,
                                       'contextIndex' => 2,
                                       'dragging' => bless( do{\(my $o = 0)}, 'Cpanel::JSON::XS::Boolean' ),
                                       'offsetX' => 73
                                     },
                                     {
                                       'width' => 102,
                                       'offsetY' => 34,
                                       'x' => 103,
                                       'height' => 102,
                                       'image' => {},
                                       'y' => 17,
                                       'contextIndex' => 3,
                                       'dragging' => $VAR1->{'stateForUserNumber7'}[0]{'dragging'},
                                       'offsetX' => 46
                                     }
                                   ]
        };
[info] *** Request 15 (1.250/s) [17427] [Fri Dec  6 00:02:22 2013] ***
[debug] Path is "homescreen"
[debug] "POST" request for "homescreen" from "192.168.1.100"
[debug] Rendering template "homescreen.html"
[debug] Response Code: 200; Content-Type: text/html; charset=utf-8; Content-Length: 7010
[info] Request took 0.025343s (39.459/s)
.------------------------------------------------------------+-----------.
| Action                                                     | Time      |
+------------------------------------------------------------+-----------+
| /homescreen                                                | 0.014044s |
| /end                                                       | 0.001992s |
|  -> Organiser::View::TT->process                           | 0.001058s |
'------------------------------------------------------------+-----------'

更多更新

当使用-d时,在开发服务器输出中会显示以下错误:

Caught exception in Organiser::Controller::Homescreen->homescreen "Can't use an undefined value as a HASH reference at /home/fred/Organiser/script/../lib/Organiser/Controller/Homescreen.pm line 21."

当运行开发服务器时,我从Stack Trace收到的错误如下:

Stack Trace
Package                             Line    File
Organiser::Controller::Homescreen   21    /home/fred/Organiser/lib/Organiser/Controller/Homescreen.pm

18: 
19: print STDERR Dumper $c->req->body_data; 
20: 
21: foreach my $data (@{$c->req->body_data->{stateForUserNumber7}}) {     <-- it highlights in bold this line
22:      print "DOLLAR DATA $data\n"; 
23: } 
24:

Organiser::Controller::Root     17  /home/fred/Organiser/lib/Organiser/Controller/Root.pm

14: sub index :Path :Args(0) { 
15: my ( $self, $c ) = @_; 
16: 
17: $c->forward('homescreen');       <-- it highlights in bold this line
18: 
19: } 
20: 

使用Firebug,这是发生的POST请求(在我注释掉导致错误的foreach之后)

Source
{"stateForUserNumber7":[{"dragging":false,"contextIndex":4,"image":{},"x":108,"y":4,"width":102,"height":102,"offsetX":45,"offsetY":65}]}

它始终是stateForUserNumber7(我应该将其命名为master_user或其他更合适的名称)


你可以尝试在控制器中添加以下代码:'print STDERR Dumper $c->req->params;' - alex
1
@mob - 我确实尝试了$c->req->body_data,因为我在你上面展示的文档中看到了这行代码,但是当我使用它时,我会得到一个错误信息:“无法通过包“Catalyst::Request”定位对象方法“body_data”...”。而其他的例如$c->$req->body_parameters则不会产生这个错误? - yonetpkbji
那么你是否正在使用最新版本的 Catalyst?自 5.90049_003 版本(2013 年 9 月 20 日)以来,body_data 方法是新的。 - mob
@mob - 我的版本显示“由 Catalyst 5.90042 提供动力”。 - yonetpkbji
然后,您可以升级(以便使用body_data),或者使用$c->req->body并自行解析JSON。 - mob
显示剩余8条评论
3个回答

4
虽然内置的日志记录工具在创建持久化日志和进行分析时非常好用,但我发现它对于基本的调试不太有用。近来我一直在使用易于安装的Devel::Dwarn。这就像一个warn dumper,而且非常整洁地打印出来。
如果您这样启动应用程序
perl -MDevel::Dwarn ...

你可以使用 Dwarn $ref 来获取漂亮的 stderr 输出:
Dwarn $c->req->body_data

这应该给你一个清晰的审查。此外,如果你去掉 -MDevel::Dwarn,你会发现在所有意外留下 Dwarn 的地方会出现错误 (上个月 Runner 002 发布时应该这么做)。

如果这让你感到困惑,请尝试:

(在 cpanm Devel::Dwarn 后)

use Devel::Dwarn;
Dwarn $c->req->body_data

你需要在控制台获得深度结构的输出。祝好运!


2

我希望你的另一个回答(有希望)对于任何调试Catalyst / JSON问题都是有用的信息。但是问题随后的更新显示这里的问题实际上完全不同。

在这里没有足够的操作来支持所需的功能。如果index操作转发到homescreen操作,后者呈现homescreen.html模板,则$.json()调用必须是另一种负责处理数据保存请求而仅此而已的操作。

package MyProject::Controller::Homescreen;

use strict;
use warnings;
use parent 'Catalyst::Controller';
use JSON;

sub homescreen :Path('/homescreen') :Args(0)  {

    my ( $self, $c ) = @_;
    $c->stash({title => 'Home Screen',
               pagetype => 'html',
               template => 'homescreen.html'
    });
}

sub savecanvasstates :Local {
    my ($self, $c, @args) = @_;
    my $p = $c->req->param->{stateForUserNumber7} || die "wrong parameters!";
    my $result = {};
    foreach my $r (@{$p}) {
        # ... do something with $r->{x} etc
        $result->{output} = # construct whatever object the ajax caller expects
    }
    $c->res->body(to_json($result))
}

1;

在附近的 JavaScript 中:

$("#saveCanvasStates").click(function () {
    // button to save canvas states to a database

    // Serialize the states array
    var JsonStringForTransport = JSON.stringify({stateForUserNumber7: states});

    // POST the JSON to the server
    var thePost = $.ajax({
        url: 'savecanvasstates',
        type: 'POST',
        dataType: 'json',
        data: JsonStringForTransport,
        contentType: 'application/json; charset=utf-8',
        success: function(data, textStatus){ ... }
        error: function(XMLHttpRequest, textStatus, errorThrown){ ... }
    });
});

我希望这能让你所要做的更加清晰明了。

我在CPAN的Catalyst::Request中发现了这个:$req->body_data - Catalyst将解析类型为'application/json'的传入数据,并通过此方法返回对该数据的访问。 这可能是我在使用print STDERR Dumper $c->req->params;时什么也没有得到,但在使用print STDERR Dumper $c->req->body_data;时却有结果的原因? 我理解这意味着body_data自动处理application/json - yonetpkbji
如果你原始帖子中所写的内容准确反映了你的软件,那么设计上存在根本性问题。在同一操作中支持AJAX调用和默认模板渲染是不可能的,除非你做一些非常奇怪的事情。 - RET
最新和最好的 Catalyst 版本确实可以自动读取和返回 JSON,但这并不能解决您提供的代码问题。它只能在一定程度上简化 savecanvasstates 操作。 - RET
我尝试了你的新答案...它似乎从未进入savecanvasstates子程序。我在子程序/操作中添加了一些打印语句print STDERR "HELLO\n";,但它们从未在开发服务器的调试输出上打印出来?非常感谢。 - yonetpkbji
很高兴听到这个消息 - 从原帖中无法清楚地了解到确切的URI架构,不知道应该是:Local、:Path还是其他一些操作类型。请勾选和/或点赞解决了您问题的答案。 - RET
显示剩余2条评论

1
您的 Catalyst 应用程序不会接收 JSON。`dataType` 参数告诉 `$.ajax()` 如何解析从服务器返回的数据。发送到服务器的是一个标准的 GET 或 POST 请求,带有 HTML 表单参数。
当 Catalyst 接收它们时,它们就是普通的键,位于 `$c->req->params` 内,非常适合您想要进行的任何数据库操作。
因此,如果
/* JS */
JsonStringForTransport = { foo: "bar", baz: "quux" };

然后在催化剂端,您将收到:
# perl
my $foo = $c->req->params->{foo}; # or $c->req->body_params->{foo}
if ($c->req->params->{baz} eq "quux"){ ... }

...等等。

更新

所以body_data包含一个哈希(不出所料),其中有一个键stateForUserNumber7,它包含一个哈希数组的引用。这就是Dumper输出告诉你的。

你应该能够使用以下方式访问body_data

my $first_x = $c->req->body_data->{stateForUserNumber7}->[0]->{x};
print STDERR "first_x = $first_x\n"; # should print first_x = 11

然而,更有可能的是你想要做类似这样的事情:
foreach my $data (@{$c->req->body_data->{stateForUserNumber7}}) {
    # do something with $data->{x}, $data->{y}, $data->{height} etc
}

更新

jnap2013 Catalyst Advent Calendar上发表了一篇非常好的文章,介绍了如何使用body_data。如果你在你的应用程序中使用这个功能而不是更传统的params,你绝对应该花时间阅读和吸收它。


更新

抱歉,之前关于dataType的说法有误,我已经纠正了。但这并不改变我的答案的基本原则。

我认为你需要自省一下body_data,找出问题出在哪里。它是否始终是'stateForUserNumber7',还是每个请求都不同?这个问题是什么呢?

$c->log->debug("body_params keys: " _ join(", ", keys %{ $c->req->body_data }));

生产?

你必须使用开发服务器(即 bin/myapp_server.pl)以便获得完整的调试输出来帮助你。不推荐通过Apache或FastCGI进行调试。


非常好,你在顶部的解释非常有用,谢谢。从$c->req->body_data数据结构中打印特定元素或循环遍历并打印所有元素的正确方法是什么?感谢您的帮助。 - yonetpkbji
如果您只是想将它们转储出来以进行检查,请使用 $c->log->debug("body_data: " . Dumper($c->req->body_data));。但是GET和POST参数应该在您的服务器日志中可见 - 每个请求周期的输出中都有一个表格。 - RET
如果我想要把body_data数据结构的特定元素存储到一个变量中,或者存储/打印body_data数据结构的特定分支,该怎么办?我该如何迭代这个数据结构中的元素?谢谢您的帮助。 - yonetpkbji
这只是一个数据结构。看一下并查看 keys %{$c->req->body_data} 的返回结果。它应该像我的答案中的 foo 和 baz 示例一样简单。 - RET
你确定有 body_data 可以显示吗?在开发服务器日志中,你得到了什么输出?顺便说一句,在 Catalyst 中绝对不应该使用 print(默认为 STDOUT),它会对返回给客户端的内容造成严重影响。使用 print STDERR 就可以了。 - RET
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接