ฉันจะโพสต์ข้อมูลแบบฟอร์มด้วย fetch api ได้อย่างไร


118

รหัสของฉัน:

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",
}

สิ่งที่ฉันต้องการคือ $ ("# form") serialize () ใน jQuery (โดยไม่ใช้ jQuery) หรือวิธีถอดรหัส mulitpart / form-data ในคอนโทรลเลอร์ ขอบคุณสำหรับคำตอบของคุณ


ปัญหาเกี่ยวกับการใช้FormDataคืออะไร?
แขก 271314

1
ฉันต้องการโพสต์เป็น "email=test@example.com&password=pw" เป็นไปได้ไหม?
Zack

1
“ ฉันไม่รู้ว่าทำไมจำนวนในขอบเขตจึงเปลี่ยนไปทุกครั้งที่ส่ง…” - ตัวระบุขอบเขตเป็นเพียงตัวระบุแบบสุ่มอาจเป็นอะไรก็ได้และไม่มีความหมายในตัวมันเอง ดังนั้นจึงไม่มีอะไรผิดในการเลือกหมายเลขสุ่มที่นั่น (ซึ่งเป็นสิ่งที่ลูกค้ามักทำ)
โผล่

คำตอบ:


149

หากต้องการอ้างMDN ใน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) คุณสามารถสร้างURLSearchParamsวัตถุจากFormDataวัตถุ:

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ส่วนหัวด้วยตัวเอง


ตามที่ระบุไว้โดยพระ - เวลาในความคิดเห็นคุณยังสามารถสร้างURLSearchParamsและส่งผ่านFormDataวัตถุได้โดยตรงแทนที่จะต่อท้ายค่าในการวนซ้ำ:

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

อย่างไรก็ตามยังมีการสนับสนุนการทดลองในเบราว์เซอร์ดังนั้นโปรดทดสอบอย่างถูกต้องก่อนใช้งาน


18
คุณยังสามารถใช้วัตถุหรือเพียงแค่FormDataในตัวสร้างโดยตรงแทนการวนซ้ำ:new URLSearchParams(new FormData(formElement))
เวลาพระ -

@ พระ - เวลาในขณะที่เขียนคำตอบนั้นข้อโต้แย้งของผู้สร้างURLSearchParamsเป็นเรื่องใหม่มากและได้รับการสนับสนุนอย่าง จำกัด
โผล่

3
ขออภัยนั่นไม่ใช่เรื่องร้องเรียน แต่เป็นเพียงบันทึกถึงทุกคนที่จะอ่านสิ่งนี้ในอนาคต
พระ - เวลา

1
@Prasanth คุณอาจจะระบุเนื้อหาพิมพ์ตัวเองอย่างชัดเจน แต่คุณต้องเลือกที่ถูกต้องอย่างใดอย่างหนึ่ง มันง่ายกว่าที่จะทิ้งมันไว้และfetchดูแลมันให้คุณ
โผล่

1
@chovy URLSearchParamsสร้างขึ้นในเบราว์เซอร์ที่ทันสมัยที่สุดในฐานะวัตถุระดับโลกและยังทำงานจากโหนด
โผล่

68

ลูกค้า

อย่าตั้งค่าส่วนหัวประเภทเนื้อหา

// 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; }
}

4
แม้ว่าจะใช้งานได้ แต่จะไม่ส่งข้อมูลตามapplication/x-www-form-urlencodedที่ OP ขอ
โผล่

5
สำหรับฉันมันใช้งานได้เมื่อฉันลบ Content-Typeออกจากส่วนหัวและปล่อยให้เบราว์เซอร์ทำงานโดยอัตโนมัติ ขอบคุณ!
คริส

ขอบคุณ @regnauld พยายามทำงานนี้มาตลอดทั้งวัน!
ak85

1
หากคุณไม่ได้ตั้งค่า "Content-type" สำหรับ Fetch ระบบจะตั้งค่าเป็นmultipart/form-dataซึ่งเป็นสิ่งที่ควรเป็นสำหรับข้อมูลแบบฟอร์ม! จากนั้นคุณสามารถใช้multerใน expressjs เพื่อแยกวิเคราะห์รูปแบบข้อมูลนั้นได้อย่างง่ายดาย
kyw

23

คุณสามารถตั้งค่า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>


2
Reflect.apply(params.set, params, props)params.set(props[0], props[1])เป็นวิธีที่ไม่สามารถอ่านได้โดยเฉพาะอย่างยิ่งการพูด
โผล่

@poke Reflect.apply(params.set, params, props)สามารถอ่านได้จากมุมมองที่นี่
แขก 271314

นี่ดูเหมือนจะเป็นคำตอบเดียวที่ใช้ได้: / ขอบคุณ! :)
OZZIE

0

ใช้FormDataและfetchเพื่อรับและส่งข้อมูล


0
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"
  });
 }

7
โดยทั่วไปโค้ดเท่านั้นที่สามารถปรับปรุงคำตอบได้โดยการเพิ่มคำอธิบายวิธีการทำงานและเหตุผล เมื่อเพิ่มคำตอบให้กับคำถามเก่าสองปีด้วยคำตอบที่มีอยู่สิ่งสำคัญคือต้องชี้ให้เห็นแง่มุมใหม่ของคำถามที่อยู่ในคำตอบของคุณ
Jason Aller
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.