最新消息:

HTTP 的 POST 参数提交和上传的不同与 Mojolicious 的实现.

Perl admin 4957浏览 0评论

对于 HTTP 协议, 我们在使用 POST 上传的时候, 其实是有好几种不同的处理方式的, 所以对于客户端和服务器端, 也分别都有不同的处理. 正常普通的网页在提交参数上传到服务器的时候, 主要会根据内容的不同来使用不同的处理. 所体现在不同的地方在 Content-Type 的类型.
比如我们常常用 Mojolicious 处理这类接收到的参数和内容的时候, 会让很多人晕掉, 所以我在这, 基于协议的头, 来给大家介绍一下在参数和上传的时候有什么不同.
客户端, 比如浏览器网页中的 form 的表格的参数的不同, 客户端比如 Linux 命令行的 curl 的参数的不同和程序接口提交参数的不同, HTTP 协议在上传的时候, 大约会有三种不同, 这些体现在 Content-Type 的三种类型:

  1. application/x-www-form-urlencoded
  2. multipart/form-data
  3. post 的 body 的内容

下面我们来详细介绍

1. application/x-www-form-urlencoded 默认

浏览器:  在 HTML 中 form 有个参数是 enctype 属性用于指定编码方式, 常用有前面讲的两种: application/x-www-form-urlencoded 和 multipart/form-data. 但默认的时候, 我们并不指定. 不指定的时候, 默认是 “application/x-www-form-urlencoded” , 所以其实, 我们平时都是使用的这种格式来提交数据. 因为是默认就不写出来了. 注意, 这个会对空格和特别的符号进行 url 的 encode.

程序: 我们现在以 Mojo::UserAgent 这个模块为例子, 我们提交一个参数 args 值为 test.

$ua->post('http://www.php-oa.com/a/b' => form => { args => 'test'});

命令:

$curl -svo /dev/null -d "args=test" http://www.php-oa.com/a/b

HTTP 协议状态
这个时候所发送的 HTTP 的头和内容分别如下. body 中会存着参数, 会有一个特别的 Header.

-- Blocking request (http://www.php-oa.com/a/b)
-- Connect (http:www.php-oa.com:80)
-- Client >>> Server (http://www.php-oa.com/a/b)
POST /a/b HTTP/1.1
User-Agent: Mojolicious (Perl)
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
Content-Length: 9
Host: www.php-oa.com

args=test

服务端接收方式
这个时候, 服务器会根据因为是 POST 的方法, 并且头部的 Content-Type: application/x-www-form-urlencoded 会去解析 body 的参数. 这样在 Mojolicious 服务器

post '/a/b' => sub {
  my $self = shift;
  my $foo  = $self->param('args');
  $self->render(text => "Hello from $foo.");
}

2. multipart/form-data 大文件, 媒体文件

对于比较大的, 有一些二进制数据和象视频文件之类大文件, 建议使用这种方式上传.
浏览器:
普通的 HTTP 的写法如果要使用 enctype 的话, 只要象下面一样就行.

<form action="/path/to/login" enctype="multipart/form-data">
  <input disabled="disabled" name="first_name" type="text" />
  <input value="Ok" type="submit" />
</form>

客户端:
在 Mojo::UserAgent 考虑得非常周全, 当你提交的内容中包含二进制文件之类时, 就会自动帮你转换成 “multipart/form-data” 格式提交. 这个格式会生成一个随机字符来分割不同参数. 区分是否使用这种格式主要是, 当你提交的参数中, 又是一个引用, 并且引中可以使用 content 来指定内容或者 file 来指定路径.

$ua->post('http://www.php-oa.com/a/b' => form => { args => 'test', args_file => { file => '/root/.bash_history' } });

# or
$ua->post('http://www.php-oa.com/a/b' => form => { args => 'test', args_file => { content => 'test' } });

HTTP 协议状态
这个地方我们可以见到 Content-Type: multipart/form-data 的请求头, 告诉文件和参数是这种格式上传过来的.并且 boundary 用于指定一个参数之间的分割符.

-- Blocking request (http://www.php-oa.com/a/b)
-- Connect (http:www.php-oa.com:80)
-- Client >>> Server (http://www.php-oa.com/a/b)
POST /a/b HTTP/1.1
User-Agent: Mojolicious (Perl)
Content-Type: multipart/form-data; boundary=WRoHX
Connection: keep-alive
Accept-Encoding: gzip
Content-Length: 14428
Host: www.php-oa.com

--WRoHX
Content-Disposition: form-data; name="args"
test
--WRoHX
Content-Disposition: form-data; name="args_file"; filename=".bash_history"
........文件本身

服务器接收方式
在后端的服务器接收的时候 Mojolicious 想得非常周到. 对于这种格式能自动解析, 并且全程异步.不会多占内存. 这个会自动给大的文件使用一个叫 Mojo::Upload 的对象来处理, 我们可以通过 $self->req->upload(‘args_file’); 这个方法取得这个内容的对象, 这个内容的对象是Mojo::Asset::File 这个对象, 存文件和取大文件之类可以直接调用.

post '/a/b' => sub {
  my $self = shift;
  my $upload = $self->req->upload('args_file');
  my $foo  = $self->param('args');
  $self->render(text => "Hello from $foo.");
}

3. POST 的 body 的内容

最后一种, 是有时我们做大文件上传, 和提交内容之类.这个时候, 整个 body 都是文件本体. 参数象 get 一样通过 url 传过去.
这个就不用抓头了, 没任何转换, 直接整个 body 是个大文件.
客户端提交:
我们来看看客户端在这个时候怎么上传送. 同样,  我们使用 Mojo::UserAgent 为例子.

my $ua = Mojo::UserAgent->new;
$ua->transactor->add_generator(stream => sub {
    my ($transactor, $tx, $path) = @_;
    $tx->req->content->asset(Mojo::Asset::File->new(path => $path));
});
$ua->post('http://www.php-oa.com/a/b' => stream => '/root/.bash_history' );

服务器接收
这个时候, 在服务器端怎么接收啦?

post '/a/b' => sub {
  my $self = shift;
  my $body = $self->req->body;
  my $foo  = $self->param('args');
  $self->render(text => "Hello from $foo.");
}

这个, 我们直接取请求的 body 就可以了, 但这有个小问题, 这是这个文件上传完, 这个 body 会存着所有的文件, 比如这个上传的文件有 1G , 这个 1G 就都会占着内存. 这个情况, Mojolicious 并没有实现事件来根据块取文件. 晚点, 我有个有于大文件上传的文章, 会分享我在 Mojolicious 中实现异步以块方式存储文件. 这样用户上传多少, 我存多少, 并不会占用更多的内存.

好了整个三种方式都介绍完了, 大家一定注意区分哦 .

转载请注明:爱开源 » HTTP 的 POST 参数提交和上传的不同与 Mojolicious 的实现.

您必须 登录 才能发表评论!