为何文中最后的 patch 方法要使用 x-www-form-urlencoded 而不是 form-data?

我们知道,HTTP请求中form-data 既可以上传键值对,也可以上传文件,x-www-form-urlencoded只能传键值对。

文中最后部分patch方法那里,去更新User信息时,只传键值对而无文件,理论上应该选择form-data和x-www-form-urlencoded都可以。实际测试只有x-www-form-urlencoded能成功更改用户信息。这是为什么呢?

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 8

『理论上应该选择 form-data 和 x-www-form-urlencoded 都可以』 哪来的理论?

http 协议 put 和 patch 只能使用 x-www-form-urlencoded

11个月前 评论

@liyu001989 老师,我把这个大致读了一下,

  • In addition, it defines the default ENCTYPE attribute of the FORM element using the POST METHOD to have the default value "application/x-www-form-urlencoded".
  • When the user completes the form, and selects the SUBMIT element, the
    browser should send the form data and the content of the selected
    files. The encoding type application/x-www-form-urlencoded is
    inefficient for sending large quantities of binary data or text
    containing non-ASCII characters. Thus, a new media type,
    multipart/form-data, is proposed as a way of efficiently sending the
    values associated with a filled-out form from client to server.
  • 5.8 File transfer with ENCTYPE=x-www-form-urlencoded
    If a form contains <INPUT TYPE=file> elements but does not contain an
    ENCTYPE in the enclosing <FORM>, the behavior is not specified. It
    is probably inappropriate to attempt to URN-encode large quantities
    of data to servers that don't expect it.

似乎此文是在说明ENCTYPE=x-www-form-urlencoded用来上传图片是不够的,因此引入了ENCTYPE=multipart/form-data

我再次看了源码:

  • 在\Symfony\Component\HttpFoundation\Request::createFromGlobals方法中,可以看到$request->getContent()这个方法,而这个方法中有这么一段代码:

        if (null === $this->content || false === $this->content) {
            $this->content = file_get_contents('php://input');
        }
  • 参考这里,可知 php://input is not available with enctype="multipart/form-data".

  • enctype="multipart/form-data"时,file_get_contents('php://input') 为类似这样的字符串:

    "avatar_image_id"
    
    1
    ----------------------------007603761275937497486210
    Content-Disposition: form-data; name="name"
    
    hustnzj
    ----------------------------007603761275937497486210
    Content-Disposition: form-data; name="email"
    
    123@qq.com
    ----------------------------007603761275937497486210
    Content-Disposition: form-data; name="introduction"
    
    hehe
    ----------------------------007603761275937497486210--
  • 而enctype="x-www-form-urlencoded"时,file_get_contents('php://input') 为这样的字符串。

    avatar_image_id=1&name=hustnzj2&email=123%40qq.com&introduction=hehe
  • 因此,ENCTYPE需要为x-www-form-urlencoded,才能进行下一步的parse_str操作。

8个月前 评论

『理论上应该选择 form-data 和 x-www-form-urlencoded 都可以』 哪来的理论?

http 协议 put 和 patch 只能使用 x-www-form-urlencoded

11个月前 评论

@liyu001989 之前的教程里,PUT、PATCH、DELETE 请求是通过在表单里伪造一个 _method 参数,Api 的这几个请求不是放在表单参数里的吗?

8个月前 评论

@liyu001989 老师,“http 协议 put 和 patch 只能使用 x-www-form-urlencoded”,我只找到了这个依据:
https://developer.mozilla.org/en-US/docs/W... , 里面写了"Allowed in HTML forms: No"
有没有更权威的依据?谢谢。

8个月前 评论
  • 如果不考虑理论的话,或许光看源码也可以得出这个结论。

    • 首先,\App\Http\Controllers\Api\UsersController::update方法中注入的是UserRequest,而UserRequest的最顶层类为\Symfony\Component\HttpFoundation\Request。
    • 在\Symfony\Component\HttpFoundation\Request中,有一个createFromGlobals方法,作用是Creates a new request with values from PHP's super globals. 代码:

      /**
      * Creates a new request with values from PHP's super globals.
      *
      * @return static
      */
      public static function createFromGlobals()
      {
      $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
      
      if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
          && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
      ) {
          parse_str($request->getContent(), $data);
          $request->request = new ParameterBag($data);
      }
      
      return $request;
      }
    • 可以看出,只有当CONTENT_TYPE为application/x-www-form-urlencoded,且REQUEST_METHOD为'PUT', 'DELETE', 'PATCH'时,才会将参数放入$request中。
    • 再回到\App\Http\Controllers\Api\UsersController::update方法中来,如果$request->request为空,那么$attributes = $request->only(['name', 'email', 'introduction']);的结果就是空,$user->update($attributes);就是什么也没有update。
8个月前 评论

@hustnzj 我表述的可能有误解,http 默认情况下就是使用 x-www-form-urlencoded ,需要上传二进制数据的时候需要使用 post 并使用 form-data 。

你看一看看 https://tools.ietf.org/html/rfc1867#sectio...

8个月前 评论

@liyu001989 老师,我把这个大致读了一下,

  • In addition, it defines the default ENCTYPE attribute of the FORM element using the POST METHOD to have the default value "application/x-www-form-urlencoded".
  • When the user completes the form, and selects the SUBMIT element, the
    browser should send the form data and the content of the selected
    files. The encoding type application/x-www-form-urlencoded is
    inefficient for sending large quantities of binary data or text
    containing non-ASCII characters. Thus, a new media type,
    multipart/form-data, is proposed as a way of efficiently sending the
    values associated with a filled-out form from client to server.
  • 5.8 File transfer with ENCTYPE=x-www-form-urlencoded
    If a form contains <INPUT TYPE=file> elements but does not contain an
    ENCTYPE in the enclosing <FORM>, the behavior is not specified. It
    is probably inappropriate to attempt to URN-encode large quantities
    of data to servers that don't expect it.

似乎此文是在说明ENCTYPE=x-www-form-urlencoded用来上传图片是不够的,因此引入了ENCTYPE=multipart/form-data

我再次看了源码:

  • 在\Symfony\Component\HttpFoundation\Request::createFromGlobals方法中,可以看到$request->getContent()这个方法,而这个方法中有这么一段代码:

        if (null === $this->content || false === $this->content) {
            $this->content = file_get_contents('php://input');
        }
  • 参考这里,可知 php://input is not available with enctype="multipart/form-data".

  • enctype="multipart/form-data"时,file_get_contents('php://input') 为类似这样的字符串:

    "avatar_image_id"
    
    1
    ----------------------------007603761275937497486210
    Content-Disposition: form-data; name="name"
    
    hustnzj
    ----------------------------007603761275937497486210
    Content-Disposition: form-data; name="email"
    
    123@qq.com
    ----------------------------007603761275937497486210
    Content-Disposition: form-data; name="introduction"
    
    hehe
    ----------------------------007603761275937497486210--
  • 而enctype="x-www-form-urlencoded"时,file_get_contents('php://input') 为这样的字符串。

    avatar_image_id=1&name=hustnzj2&email=123%40qq.com&introduction=hehe
  • 因此,ENCTYPE需要为x-www-form-urlencoded,才能进行下一步的parse_str操作。

8个月前 评论

@hustnzj 学习就怕你这种较真的人

5个月前 评论

php 中 $_POST这样写的:当 HTTP POST 请求的 Content-Type 是 application/x-www-form-urlencoded 或 multipart/form-data 时,会将变量以关联数组形式传入当前脚本。浏览器中,只能是 GET 或者 POST 请求方法,postman 则可以是 PUT 或者 PATCH。 当传统 http 发起 PATCH 请求(表单页面通过 _method 构造了一个假的提交方法,用来匹配路由方法,本质还是 POST 方法),laravel 是通过 $_POST 获取表单数据。postman 等非传统的方式用 PUT 后者 PATCH 提交表单,$_POST 获取不到表单数据,laravel 会判断请求方法,如果是 PUT 或者 PATCH,并且 content-type:x-www-form-urlencoded ,就会从 php://input 获取表单数据,至于 symfony 为什么这么实现,可能是为了遵守规范。
如果非要在 postman 中用 multipart/form-data,可以用 POST 方法模拟网页提交表单数据,并在表单数据中加入变量 _method=PATCH。

1周前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

社区文档:

将托管在 packagist.org 和 github.com 的扩展包使用国内 CDN 加速
GitHub Laravel 扩展包 TOP 250
速查表方便快速查询框架功能,支持手机访问,支持中英文版本
Laravel 中文文档,由社区用户翻译和维护,将会保持一直更新
此文档的目的,就是为了提高技术团队的凝聚力、一致性和生产效率。
开发环境的部署,开发者工具的选择,适用于 Mac 和 Windows。
浓缩过后的精华
Laravel Nova 后台管理面板文档的中文翻译
Lumen 中文文档,由社区用户翻译和维护,将会保持一直更新
Laravel 下知名扩展包 Dingo API 的中文文档,Laravel API 开发必知必会