【问题标题】:How to make Mason2 UTF-8 clean?如何使 Mason2 UTF-8 干净?
【发布时间】:2011-08-17 01:07:27
【问题描述】:

重新表述问题,因为

评论:这个问题已经获得了“热门问题徽章”,所以可能我不是唯一一个绝望的人。 :)

不幸的是,演示完整问题堆栈会导致一个很长的问题,而且它非常具体Mason

首先,只有意见的部分:)

我使用 HTML::Mason 已经很久了,现在尝试使用 Mason2。 PoetMason 是 CPAN 中最先进的框架。 找不到任何可比的东西,开箱即用的东西允许写得如此干净/但非常容易破解:)/网络应用程序, 包括许多电池(日志记录、缓存、配置管理、基于原生 PGSI 等...)

不幸的是,作者并不关心这个词的其余部分, 例如默认情况下,它仅基于 ascii, 没有任何手册、常见问题解答或关于以下方面的建议:如何将其与 unicode 一起使用

现在是事实。演示。创建一个诗人应用程序:

poet new my #the "my" directory is the $poet_root
mkdir -p my/comps/xls
cd my/comps/xls

并在dhandler.mc 中添加以下内容(将演示这两个基本问题)

<%class>
    has 'dwl';
    use Excel::Writer::XLSX;
</%class>
<%init>
    my $file = $m->path_info;

    $file =~ s/[^\w\.]//g;
    my $cell = lc join ' ', "ÅNGSTRÖM", "in the", $file;

    if( $.dwl ) {
        #create xlsx in the memory
        my $excel;
        open my $fh, '>', \$excel or die "Failed open scalar: $!";
        my $workbook  = Excel::Writer::XLSX->new( $excel );
        my $worksheet = $workbook->add_worksheet();
        $worksheet->write(0, 0, $cell);
        $workbook->close();

        #poet/mason output
        $m->clear_buffer;
        $m->res->content_type("application/vnd.ms-excel");
        $m->print($excel);
        $m->abort();
    }
</%init>
<table border=1>
<tr><td><% $cell %></td></tr>
</table>
<a href="?dwl=yes">download <% $file %></a>

并运行应用程序

../bin/run.pl

转到http://0:5000/xls/hello.xlsx,您将获得:

+----------------------------+
| ÅngstrÖm in the hello.xlsx |
+----------------------------+
download hello.xlsx

点击download hello.xlsx,您将在下载中获得hello.xlsx

上面演示了第一个问题, 例如组件的源不在use utf8;“下”, 所以lc 不理解字符。

第二个问题如下,试试 [http://0:5000/xls/hélló.xlsx],或 http://0:5000/xls/h%C3%A9ll%C3%B3.xlsx 你会看到:

+--------------------------+
| ÅngstrÖm in the hll.xlsx |
+--------------------------+
download hll.xlsx
#note the wrong filename

当然,输入(path_info)没有被解码,脚本使用 utf8 编码的八位字节而不是 perl 字符。

所以,通过将use utf8; 添加到&lt;%class%&gt; 中,告诉 perl -“源代码在 utf8 中”,结果

+--------------------------+
| �ngstr�m in the hll.xlsx |
+--------------------------+
download hll.xlsx

添加use feature 'unicode_strings'(或use 5.014;)更糟糕:

+----------------------------+
| �ngstr�m in the h�ll�.xlsx |
+----------------------------+
download h�ll�.xlsx

当然,源现在包含宽字符,输出时需要Encode::encode_utf8

可以尝试使用以下过滤器:

<%filter uencode><% Encode::encode_utf8($yield->()) %></%filter>

并过滤整个输出:

% $.uencode {{
<table border=1>
<tr><td><% $cell %></td></tr>
</table>
<a href="?dwl=yes">download <% $file %></a>
% }}

但这只是部分帮助,因为需要注意&lt;%init%&gt;&lt;%perl%&gt; 块中的编码。 在许多地方对 perl 代码内部进行编码/解码(阅读:不是在边界)会导致代码冗长。

编码/解码应该清楚地在某处完成 Poet/Mason 边界——当然,Plack 在字节级别上运行。


部分解决方案。

很高兴,Poet 巧妙地允许修改它(和梅森)的部分,所以, 在$poet_root/lib/My/Mason 中,您可以将Compilation.pm 修改为:

override 'output_class_header' => sub {
    return join("\n",
        super(), qq(
        use 5.014;
        use utf8;
        use Encode;
        )
    );
};

什么会将所需的序言插入到每个 Mason 组件中。 (不要忘记触摸每个组件,或者干脆从$poet_root/data/obj 中删除已编译的对象。

您也可以尝试在边界处理请求/响应, 通过将$poet_root/lib/My/Mason/Request.pm 编辑为:

#found this code somewhere on the net
use Encode;
override 'run' => sub {
    my($self, $path, $args) = @_;

    #decode values - but still missing the "keys" decode
    foreach my $k (keys %$args) {
        $args->set($k, decode_utf8($args->get($k)));
    }

    my $result = super();

    #encode the output - BUT THIS BREAKS the inline XLS
    $result->output( encode_utf8($result->output()) );
    return $result;
};

对所有内容进行编码是一个错误的策略,它会中断,例如XLS。

所以,4 年后(我在 2011 年问了原始问题)仍然不知道 :( 如何在 Mason2 应用程序中正确使用 unicode,但仍然不存在有关它的文档或帮助者。:(

主要问题是: - 在哪里(Moose 的方法修饰符应该修改哪些方法)以及如何正确解码输入以及输出在哪里(在 Poet/Mason 应用程序中。)

  • 但只有文本,例如text/plaintext/html 等等...
  • 执行上述“无惊喜” - 例如。什么会简单有效。 ;)

有人可以帮忙提供真实的代码吗?我应该在上面修改什么?

【问题讨论】:

    标签: perl utf-8 moose mason plack


    【解决方案1】:

    Mason2 手册介绍了 component inheritance 的工作方式,所以我认为将这个通用代码放入您的 ma​​in Base.mp 组件(所有其他组件都继承自该组件)可能会解决您的问题。

    Mason::Manual::Plugins 中描述了创建插件。

    因此,您可以构建自己的插件来修改Mason::Request,并通过覆盖request_args(),您可以返回UTF-8 解码的参数。

    编辑:

    关于 UTF-8 输出,您可以添加 Apache directive 以确保 text/plain 和 text/HTML 输出始终被解释为 UTF-8:

    AddDefaultCharset utf-8
    

    【讨论】:

    • 问题有更多部分:在输入处 - 是的 - 需要将所有 GET/POST args 的八位字节转换为 utf8(也可能是 PATH_INFO)。在输出中,最简单的是 - 发送正确的 xml/html 标头。但是在输出端,无论想发送什么到浏览器,在离开之前 Mason 都需要将 utf8 转换为八位字节,因为 Plack 会抱怨。当然,需要另一个插件,它会自动插入每个组件源“使用 utf8;” - 因为所有组件都将在代码中使用 utf8...加上“使用 open (:std :utf8);” pragma 也是..
    • 但是,感谢提示 - 修改 request_arg 方法。
    • Apache 指令不是解决方案。 Mason 可以作为 CLI 工作,并且可以在 Plack 下工作。因此,所有 utf-8 转换必须在 mason 级别完成。 (插件)。幸运的是,设置正确的标题很容易。在 mason 插件源代码中寻找帮助。我应该修改哪些类和哪些方法以及如何
    • 由于这里没有我想看到的答案,但你一个人,我奖励你赏金。 :)
    【解决方案2】:

    好的,我已经用 Firefox 测试过了。 HTML 正确显示 UTF-8 并单独保留 zip,因此应该可以在任何地方使用。

    如果您以poet new My 开头来应用您需要patch -p1 -i...path/to/thisfile.diff 的补丁。

    diff -ruN orig/my/comps/Base.mc new/my/comps/Base.mc
    --- orig/my/comps/Base.mc   2015-05-20 21:48:34.515625000 -0700
    +++ new/my/comps/Base.mc    2015-05-20 21:57:34.703125000 -0700
    @@ -2,9 +2,10 @@
     has 'title' => (default => 'My site');
     </%class>
    
    -<%augment wrap>
    -  <html>
    +<%augment wrap><!DOCTYPE html>
    +  <html lang="en-US">
         <head>
    +      <meta charset="utf-8">
           <link rel="stylesheet" href="/static/css/style.css">
     % $.Defer {{
           <title><% $.title %></title>
    diff -ruN orig/my/comps/xls/dhandler.mc new/my/comps/xls/dhandler.mc
    --- orig/my/comps/xls/dhandler.mc   1969-12-31 16:00:00.000000000 -0800
    +++ new/my/comps/xls/dhandler.mc    2015-05-20 21:53:42.796875000 -0700
    @@ -0,0 +1,30 @@
    +<%class>
    +    has 'dwl';
    +    use Excel::Writer::XLSX;
    +</%class>
    +<%init>
    +    my $file = $m->path_info;
    +    $file = decode_utf8( $file );
    +    $file =~ s/[^\w\.]//g;
    +    my $cell = lc join ' ', "ÅNGSTRÖM", "in the", $file ;
    +    if( $.dwl ) {
    +        #create xlsx in the memory
    +        my $excel;
    +        open my $fh, '>', \$excel or die "Failed open scalar: $!";
    +        my $workbook  = Excel::Writer::XLSX->new( $fh );
    +        my $worksheet = $workbook->add_worksheet();
    +        $worksheet->write(0, 0, $cell);
    +        $workbook->close();
    +
    +        #poet/mason output
    +        $m->clear_buffer;
    +        $m->res->content_type("application/vnd.ms-excel");
    +        $m->print($excel);
    +        $m->abort();
    +    }
    +</%init>
    +<table border=1>
    +<tr><td><% $cell %></td></tr>
    +</table>
    +<p> <a href="%c3%85%4e%47%53%54%52%c3%96%4d%20%68%c3%a9%6c%6c%c3%b3">ÅNGSTRÖM hélló</a>
    +<p> <a href="?dwl=yes">download <% $file %></a>
    diff -ruN orig/my/lib/My/Mason/Compilation.pm new/my/lib/My/Mason/Compilation.pm
    --- orig/my/lib/My/Mason/Compilation.pm 2015-05-20 21:48:34.937500000 -0700
    +++ new/my/lib/My/Mason/Compilation.pm  2015-05-20 21:49:54.515625000 -0700
    @@ -5,11 +5,13 @@
     extends 'Mason::Compilation';
    
     # Add customizations to Mason::Compilation here.
    -#
    -# e.g. Add Perl code to the top of every compiled component
    -#
    -# override 'output_class_header' => sub {
    -#      return join("\n", super(), 'use Foo;', 'use Bar qw(baz);');
    -# };
    -
    +override 'output_class_header' => sub {
    +    return join("\n",
    +        super(), qq(
    +        use 5.014;
    +        use utf8;
    +        use Encode;
    +        )
    +    );
    +};
     1;
    \ No newline at end of file
    diff -ruN orig/my/lib/My/Mason/Request.pm new/my/lib/My/Mason/Request.pm
    --- orig/my/lib/My/Mason/Request.pm 2015-05-20 21:48:34.968750000 -0700
    +++ new/my/lib/My/Mason/Request.pm  2015-05-20 21:55:03.093750000 -0700
    @@ -4,20 +4,27 @@
    
     extends 'Mason::Request';
    
    -# Add customizations to Mason::Request here.
    -#
    -# e.g. Perform tasks before and after each Mason request
    -#
    -# override 'run' => sub {
    -#     my $self = shift;
    -#
    -#     do_tasks_before_request();
    -#
    -#     my $result = super();
    -#
    -#     do_tasks_after_request();
    -#
    -#     return $result;
    -# };
    +use Encode qw/ encode_utf8 decode_utf8 /;
    
    -1;
    \ No newline at end of file
    +override 'run' => sub {
    +    my($self, $path, $args) = @_;
    +    foreach my $k (keys %$args) {
    +        my $v = $args->get($k);
    +        $v=decode_utf8($v);
    +        $args->set($k, $v);
    +    }
    +    my $result = super();
    +    my( $ctype, $charset ) = $self->res->headers->content_type_charset;
    +    if( ! $ctype ){
    +        $ctype = 'text/html';
    +        $charset = 'UTF-8';
    +        $self->res->content_type( "$ctype; $charset");
    +        $result->output( encode_utf8(''.( $result->output())) );
    +    } elsif( ! $charset and $ctype =~ m{text/(?:plain|html)} ){
    +        $charset = 'UTF-8';
    +        $self->res->content_type( "$ctype; $charset");
    +        $result->output( encode_utf8(''.( $result->output())) );
    +    }
    +    return $result;
    +};
    +1;
    

    【讨论】:

    • 因此,您建议在 run 检查字符集和内容类型的存在,如果尚未设置 - 将其设置为 utf8 并执行 encode(如果内容-类型为plainhtml)。好的,这个逻辑是可以接受的,因为你不想为其他mime-type 服务,比如text/css。每次都重写 run,因为提供新的 mime 类型对开发人员不是很友好。嗯,也许,我可以添加另一个(第 3 个)elseif 应该说什么:if the charset is already set - 对任何 mime 类型执行 encode,这将表明需要编码。可接受 - 但会等到赏金结束。 ;)
    • 好点,除了另一个 elsif 之外,/^text/ 可能已经足够通用了 ... 确保 $path 是像 my $result = $self-&gt;SUPER::run( decode_utf8( $path ), $args ); 这样的 unicode 也是一个好主意
    【解决方案3】:

    在 mason-users 邮件列表中有一个关于处理 UTF-8 的问题

    1. 使用 UTF-8 输出组件
    2. 处理 UTF-8 GET/POST 参数

    这是乔恩的回答:

    我希望 Mason 能够智能地处理编码,但因为我不这样做 经常使用 utf8,你和其他人将不得不帮助我 设计。

    这可能应该在插件中,例如Mason::Plugin::UTF8。

    所以对于你特别提到的事情,这样的事情可能 工作:

    package Mason::Plugin::UTF8;
    use Moose;
    with 'Mason::Plugin';
    1;
    
    package Mason::Plugin::UTF8::Request;
    use Mason::PluginRole;
    use Encode;
    
    # Encode all output in utf8 - ** only works with Mason 2.13 and beyond **
    #
    after 'process_output' => sub {
        my ($self, $outref) = @_;
        $$outref = encode_utf8( $$outref );
    };
    
    # Decode all parameters as utf8
    #
    around 'run' => sub {
        my $orig = shift;
        my $self = shift;
    
        my %params = @_;
        while (my ($key, $value) = each(%params)) {
            $value = decode_utf8($value);
        }
        $self->$orig(%params);
    }
    
    1;
    

    如果你或其他人了解这可能是最好的 utf8 问题创建了这个插件,而不是我自己。但是让我知道如果 Mason 核心中需要一些东西来使这更容易。

    恕我直言,还需要添加以下内容,以添加“use utf8;”进入每个组件。

    package Mason::Plugin::UTF8::Compilation;
    use Mason::PluginRole;
    override 'output_class_header' => sub {
        return(super() . 'use utf8;');
    };
    
    1;
    

    【讨论】:

    • $$outref = encode_utf8( $$outref ); 所在的解决方案仅部分有效。例如,它(当然)会破坏每个内联生成的Excel 文档或任何其他使用 Mason2 生成的二进制文件 - 例如不可接受 encode_utf8 一切
    • kobame 请用这个“Excel”损坏的简短示例更新您的问题......如果调用 clear_buffer+abort,您可能可以避免编码为 utf8
    猜你喜欢
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 1970-01-01
    • 2015-02-13
    • 2016-08-06
    相关资源
    最近更新 更多