perl - ¿Cómo hacer mason2 UTF-8 limpio?
moose plack (3)
Reformulando la pregunta, porque
- @opcional me preguntó
- no estaba claro y estaba vinculado a una solución basada en HTML::Mason Cuatro pasos sencillos para hacer que Mason UTF-8 Unicode se limpie con Apache, mod_perl y DBI , lo que causó confusiones
- el original tiene 4 años y mientras tanto (en 2012) se crea el "poeta"
Comentario: Esta pregunta ya obtuvo la "insignia de pregunta popular", por lo que probablemente no soy la única persona desesperada. :)
Desafortunadamente, la demostración de la pila de problemas completa lleva a una pregunta muy larga y es muy específica de Mason .
Primero, la parte de opiniones solamente :)
Estoy usando HTML :: Mason a través de las edades, y ahora trato de usar Mason2. Poet y Mason son los marcos más avanzados en el CPAN. No se encontró nada comparable, lo que fuera de la caja permite escribir de manera limpia / pero muy hackeable:) / aplicaciones web, con muchas baterías incluidas (registro, almacenamiento en caché, gestión de configuración, basado en PGSI nativo, etc.)
Desafortunadamente, al autor no le importa el resto de la palabra, por ejemplo, por defecto, solo está basado en ascii, sin ningún manual, preguntas frecuentes o consejos sobre: cómo usarlo con unicode
Ahora los hechos. Manifestación. Crear una aplicación de poeta:
poet new my #the "my" directory is the $poet_root
mkdir -p my/comps/xls
cd my/comps/xls
y agregue lo siguiente a dhandler.mc
(lo que demostrará los dos problemas básicos)
<%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>
y ejecuta la aplicación
../bin/run.pl
vaya a http: // 0: 5000 / xls / hello.xlsx y obtendrá:
+----------------------------+
| ÅngstrÖm in the hello.xlsx |
+----------------------------+
download hello.xlsx
Haciendo clic en la descarga hello.xlsx , obtendrás hello.xlsx
en las descargas.
Lo anterior demuestra el primer problema, por ejemplo, la fuente del componente no está "debajo" del use utf8;
, así que el lc
no entiende los caracteres.
El segundo problema es el siguiente, pruebe el [ http: // 0: 5000 / xls / hélló.xlsx] , o http: // 0: 5000 / xls / h% C3% A9ll% C3% B3.xlsx y lo hará ver:
+--------------------------+
| ÅngstrÖm in the hll.xlsx |
+--------------------------+
download hll.xlsx
#note the wrong filename
Por supuesto, la entrada (el path_info
) no se decodifica, el script funciona con los octetos codificados en utf8 y no con caracteres perl.
Entonces, indicando a perl: "la fuente está en utf8", agregando el use utf8;
en el <%class%>
, resultados
+--------------------------+
| �ngstr�m in the hll.xlsx |
+--------------------------+
download hll.xlsx
añadiendo la use feature ''unicode_strings''
(o use 5.014;
) aún peor:
+----------------------------+
| �ngstr�m in the h�ll�.xlsx |
+----------------------------+
download h�ll�.xlsx
Por supuesto , la fuente ahora contiene caracteres anchos, necesita Encode::encode_utf8
en la salida.
Uno podría intentar usar un filtro como:
<%filter uencode><% Encode::encode_utf8($yield->()) %></%filter>
y filtrar toda la salida:
% $.uencode {{
<table border=1>
<tr><td><% $cell %></td></tr>
</table>
<a href="?dwl=yes">download <% $file %></a>
% }}
pero esto ayuda solo parcialmente, porque hay que preocuparse por la codificación en los bloques <%init%>
o <%perl%>
. La codificación / decodificación dentro del código de Perl en muchos lugares ( leído: no en los bordes ) conduce a un código de spagethy.
La codificación / decodificación debe hacerse claramente en algún lugar en los bordes de Poet / Mason ; por supuesto, el Plack opera en el nivel de bytes.
Solución parcial.
Felizmente, el Poet permite modificar hábilmente sus partes (y las de Mason), por lo que en $poet_root/lib/My/Mason
podría modificar Compilation.pm
para:
override ''output_class_header'' => sub {
return join("/n",
super(), qq(
use 5.014;
use utf8;
use Encode;
)
);
};
lo que insertará el preámbulo deseado en cada componente de Mason. (No olvide tocar todos los componentes o simplemente elimine los objetos compilados de $poet_root/data/obj
).
También puede tratar de manejar la solicitud / respuestas en los bordes, editando $poet_root/lib/My/Mason/Request.pm
para:
#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;
};
Codificar todo es una estrategia incorrecta, se rompe, por ejemplo, el XLS.
Entonces, 4 años después (hice la pregunta original en 2011) aún no sé :( cómo usar correctamente el Mason Unicode en las aplicaciones Mason y aún no existe ninguna documentación o ayuda al respecto .(
Las preguntas principales son: - dónde (qué métodos deberían modificarse con los modificadores del método de Moose) y cómo descodificar correctamente las entradas y dónde está la salida (en la aplicación Poet / Mason).
- pero solo textuales, por ejemplo,
text/plain
otext/html
y tales ... - Haz lo anterior "sin sorpresas", por ejemplo, lo que simplemente funciona. ;)
¿Podría alguien ayudar con el código real? ¿Qué debo modificar en lo anterior?
El manual de Mason2 presenta la forma en que funciona la herencia de componentes , por lo que creo que poner este código común en su componente principal Base.mp (del cual todas las demás herencias) puede resolver su problema.
La creación de complementos se describe en Mason::Manual::Plugins .
Por lo tanto, puede crear su propio complemento que modifica Mason::Request y, al anular el request_args()
, puede devolver los parámetros descodificados de UTF-8.
Editar:
Con respecto a la salida de UTF-8, puede agregar una directiva de Apache para asegurarse de que las salidas de texto / sin formato y de texto / HTML siempre se interpreten como UTF-8:
AddDefaultCharset utf-8
En la lista de correo de mason-users había una pregunta sobre el manejo de UTF-8 para
- componentes de salida con UTF-8
- manejo de los argumentos UTF-8 GET / POST
Aquí está la respuesta de Jon:
Me gustaría que Mason manejara la codificación de manera inteligente, pero como no trabajo regularmente con utf8, usted y otros tendrán que ayudarme con el diseño.
Probablemente debería estar en un complemento, por ejemplo, Mason :: Plugin :: UTF8.
Así que para las cosas que mencionas particularmente, algo como esto podría funcionar:
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;
Probablemente sería mejor que usted u otra persona con conocimientos sobre los problemas de utf8 crearan este complemento en lugar de mí. Pero déjame saber si hay cosas necesarias en el núcleo de Mason para hacer esto más fácil.
En mi humilde opinión, es necesario agregar lo siguiente también, para agregar "use utf8;" en cada componente.
package Mason::Plugin::UTF8::Compilation;
use Mason::PluginRole;
override ''output_class_header'' => sub {
return(super() . ''use utf8;'');
};
1;
OK, he probado esto con Firefox. El HTML muestra el UTF-8 correctamente y deja el zip solo, por lo que debería funcionar en todas partes.
Si empiezas con el poet new My
para aplicar el parche, necesitas el 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;