【问题标题】:How do I post form data with fetch api?如何使用 fetch api 发布表单数据?
【发布时间】:2018-03-20 06:48:50
【问题描述】:

我的代码:

fetch("api/xxx", {
    body: new FormData(document.getElementById("form")),
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        // "Content-Type": "multipart/form-data",
    },
    method: "post",
}

我尝试使用 fetch api 发布我的表单,它发送的正文如下:

-----------------------------114782935826962
Content-Disposition: form-data; name="email"

test@example.com
-----------------------------114782935826962
Content-Disposition: form-data; name="password"

pw
-----------------------------114782935826962--

(我不知道为什么每次发送时边界中的数字都会改变......)

我希望它使用“Content-Type”发送数据:“application/x-www-form-urlencoded”,我该怎么办?或者如果我只需要处理它,我如何解码控制器中的数据?


谁回答我的问题,我知道我可以做到:

fetch("api/xxx", {
    body: "email=test@example.com&password=pw",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
    },
    method: "post",
}

我想要的是 jQuery 中的 $("#form").serialize() (不使用 jQuery)或在控制器中解码 mulitpart/form-data 的方式。不过感谢您的回答。

【问题讨论】:

  • 使用FormData有什么问题?
  • 我想将其发布为“email=test@example.com&password=pw”。有可能吗?
  • “我不知道为什么边界中的数字每次发送都会改变......” - 边界标识符只是一个随机标识符,它可以是任何东西并且可以本身没有任何意义。所以在那里选择一个随机数并没有错(这是客户通常会做的)。

标签: javascript ajax fetch-api


【解决方案1】:

引用MDN on FormData(强调我的):

FormData 接口提供了一种方法来轻松构造一组表示表单字段及其值的键/值对,然后可以使用XMLHttpRequest.send() 方法轻松发送。 如果编码类型设置为"multipart/form-data",它使用与表单相同的格式

因此,当使用FormData 时,您会将自己锁定在multipart/form-data 中。无法发送 FormData 对象作为主体,发送 multipart/form-data 格式的数据。

如果您想以application/x-www-form-urlencoded 发送数据,您必须将正文指定为 URL 编码字符串,或传递URLSearchParams 对象。遗憾的是,后者不能直接从 form 元素初始化。如果您不想自己遍历表单元素(可以使用HTMLFormElement.elements),您还可以从FormData 对象创建URLSearchParams 对象:

const data = new URLSearchParams();
for (const pair of new FormData(formElement)) {
    data.append(pair[0], pair[1]);
}

fetch(url, {
    method: 'post',
    body: data,
})
.then(…);

请注意,您不需要自己指定 Content-Type 标头。


正如 cmets 中的 monk-time 所述,您还可以创建 URLSearchParams 并直接传递 FormData 对象,而不是在循环中附加值:

const data = new URLSearchParams(new FormData(formElement));

不过,这在浏览器中仍有一些实验性支持,因此请务必在使用前对其进行正确测试。

【讨论】:

  • 您也可以在构造函数中直接使用对象或只使用FormData,而不是循环:new URLSearchParams(new FormData(formElement))
  • @monk-time 在撰写该答案时,URLSearchParams 的构造函数参数是非常新的并且支持非常有限。
  • 抱歉,这不是投诉,只是给以后会阅读此内容的每个人的说明。
  • @Prasanth 您可以自己明确指定内容类型,但您必须选择正确的。将其关闭并让fetch 为您处理会更容易。
  • 如果您需要发布FormData,则无需使用URLSearchParams fetch(url, { method: 'post', body: new FormData(form_element), })
【解决方案2】:

客户

不要设置 content-type 标头。

// Build formData object.
let formData = new FormData();
formData.append('name', 'John');
formData.append('password', 'John123');

fetch("api/SampleData",
    {
        body: formData,
        method: "post"
    });

服务器

使用FromForm 属性指定绑定源是表单数据。

[Route("api/[controller]")]
public class SampleDataController : Controller
{
    [HttpPost]
    public IActionResult Create([FromForm]UserDto dto)
    {
        return Ok();
    }
}

public class UserDto
{
    public string Name { get; set; }
    public string Password { get; set; }
}

【讨论】:

  • 虽然这有效,但它不会以application/x-www-form-urlencoded 发送数据,而这正是 OP 所要求的。
  • 对我来说,当我从标题中 REMOVED Content-Type 并让浏览器自动执行时,它就起作用了。谢谢!
  • 如果你没有为Fetch设置'Content-type',它会设置为multipart/form-data,这就是表单数据应该的!然后你可以在 expressjs 中使用multer 轻松解析该数据格式。
  • 这对我有用。我使用 Laravel 8 (Sanctum) 作为后端。
【解决方案3】:

您可以将body 设置为URLSearchParams 的实例,并将查询字符串作为参数传递

fetch("/path/to/server", {
  method:"POST"
, body:new URLSearchParams("email=test@example.com&password=pw")
})

document.forms[0].onsubmit = async(e) => {
  e.preventDefault();
  const params = new URLSearchParams([...new FormData(e.target).entries()]);
  // fetch("/path/to/server", {method:"POST", body:params})
  const response = await new Response(params).text();
  console.log(response);
}
<form>
  <input name="email" value="test@example.com">
  <input name="password" value="pw">
  <input type="submit">
</form>

【讨论】:

  • Reflect.apply(params.set, params, props) 是一种特别难以理解的表达方式 params.set(props[0], props[1])
  • @poke Reflect.apply(params.set, params, props) 从这里的角度是可读的。
  • 这似乎是这里唯一可行的答案:/谢谢! :)
  • 如果我通过 body:new URLSearchParams("img="+my5MBimage) 发送 5MB 图像 文件会怎样?
  • @PYK 在这种情况下你不能使用 application/x-www-form-urlencoded 但 multipart/form-data: application/x-www-form-urlencoded or multipart/form-data?
【解决方案4】:

使用FormDatafetch抓取和发送数据

fetch(form.action, {method:'post', body: new FormData(form)});

function send(e,form) {
  fetch(form.action, {method:'post', body: new FormData(form)});

  console.log('We send post asynchronously (AJAX)');
  e.preventDefault();
}
<form method="POST" action="myapi/send" onsubmit="send(event,this)">
    <input hidden name="csrfToken" value="a1e24s1">
    <input name="email" value="a@b.com">
    <input name="phone" value="123-456-789">
    <input type="submit">    
</form>

Look on chrome console>network before/after 'submit'

【讨论】:

  • 非常感谢,这就是我一直在寻找的东西,现在纯 JavaScript 如此简单真是令人惊讶。看看那个漂亮的 1 班轮 fetch 代码 post&lt;form&gt; 数据,我仍然很惊讶我是如何找到这个的。再见 jQuery。
  • 这里根本不重要,但隐藏输入的名称有错字。对于任何想知道为什么会有这种输入的人,csrf 代表跨站点请求伪造。
  • method: 'post' 选项无效,因为浏览器将使用传递给FormDataform 的方法属性。即使form 中没有定义方法属性,浏览器也会回退到默认的GET 方法。
  • @MarcoMannes 如果你从上面 sn-p 中的fetch 参数中删除mehtod:'post',你将得到Request with GET/HEAD method cannot have body. 异常。如果您从上面 sn-p 中的 html 中删除 method="POST"method:'post'(在 fetch 参数中)将生效 - 并且浏览器将发送 POST - 我通过修改此 sn-p 并使用 chrome>network 选项卡来检查这一点(所以实际上我们可以从 html 中删除它......但我会留下它)
  • form.action 是端点 url 吗?
【解决方案5】:

使用 fetch api,事实证明您不必包含标题“Content-type”:“multipart/form-data”。

所以下面的工作:

let formData = new FormData()
formData.append("nameField", fileToSend)

fetch(yourUrlToPost, {
   method: "POST",
   body: formData
})

请注意,对于 axios,我必须使用内容类型。

【讨论】:

  • 我正在将一个文件和一些数据从 React 发送到 Flask,直到我删除了 Content-type,它才起作用。谢谢你:)
【解决方案6】:

?‍?这些可以帮助你:

let formData = new FormData();
            formData.append("name", "John");
            formData.append("password", "John123");
            fetch("https://yourwebhook", {
              method: "POST",
              mode: "no-cors",
              cache: "no-cache",
              credentials: "same-origin",
              headers: {
                "Content-Type": "form-data"
              },
              body: formData
            });
            //router.push("/registro-completado");
          } else {
            // doc.data() will be undefined in this case
            console.log("No such document!");
          }
        })
        .catch(function(error) {
          console.log("Error getting document:", error);
        });

【讨论】:

  • 您能否再解释一下您所做的更改使其变得更好
【解决方案7】:

要添加上面的好答案,您还可以避免在 HTML 中明确设置操作并在 javascript 中使用事件处理程序,使用“this”作为创建“FormData”对象的表单

HTML 格式:

<form id="mainForm" class="" novalidate>
<!--Whatever here...-->
</form>

在你的 JS 中:

$("#mainForm").submit(function( event ) {
  event.preventDefault();
  const formData = new URLSearchParams(new FormData(this));
  fetch("http://localhost:8080/your/server",
    {   method: 'POST',
        mode : 'same-origin',
        credentials: 'same-origin' ,
        body : formData
    })
    .then(function(response) {
      return response.text()
    }).then(function(text) {
        //text is the server's response
    });
});

【讨论】:

    【解决方案8】:

    要使用 fetch api 发布表单数据, 试试这个代码,它对我有用^_^

    function card(fileUri) {
    let body = new FormData();
    let formData = new FormData();
    formData.append('file', fileUri);
    
    fetch("http://X.X.X.X:PORT/upload",
      {
          body: formData,
          method: "post"
      });
     }
    

    【讨论】:

    • 只有代码的答案通常可以通过添加一些关于它们的工作方式和原因的解释来改进。在使用现有答案添加一个两年前的问题的答案时,重要的是要指出您的答案所针对的问题的新方面。
    猜你喜欢
    • 2022-11-16
    • 1970-01-01
    • 2021-06-25
    • 2018-10-08
    • 2016-05-13
    • 1970-01-01
    • 2018-03-18
    • 2017-09-23
    • 2023-03-17
    相关资源
    最近更新 更多