2020/01/17

form + API 的兩種方式比較

一個 form post 也可以繞個一大圈


網頁傳統 form 要傳遞參數值時,會使用 action,寫法大致如下:

  <form action="http://localhost:8080/login" method="post">
    <h1>Form Action Test</h1>
    <input type="text" name="email" value="Eden" />
    <input type="password" name="password" value="L@veEden" />
    <input type="submit" value="Submit">
  </form>

可以看到 submit 時,Request 的 Content-Type 屬性是很標準的 application/x-www-form-urlencoded。



這個方式很標準,絕大部份的 Web Server 都可以接受,當然也包含了 Delphi Web Server。

時代在進步,JavaScript 標準都來到 2020,所以當然要試一下新的操作方法。

JavaScript 裡有 FormData 類別,可以建構 form 物件,寫法如下:

  <form id="login-form" onsubmit="handleSubmit(event)">
    <input type="text" name="email" value="Eden" />
    <input type="password" name="password" value="L@veEden" />
    <input type="submit" value="Submit">
  </form>
  
  <script>
  function handleSubmit(event) {
    event.preventDefault()
    let formData = new FormData(document.getElementById('login-form'))

    fetch("http://localhost:8080/login", {
      method: "POST",
      body: formData
    })
  }
  </script>

實際 submit 時 Request 的 Content-Type 屬性是 multipart/form-data,如下圖所示:




multipart/form-data 相對於 application/x-www-form-urlencoded 比較少見,故某些 Web Server 預設是不會處理 multipart/form-data 的格式內容。

既然後端需要做調整,如果前端事先做好,那後端不就更省事?

前端調整

仿照傳統規則

前端仿照傳統的作法實作,程式碼如下:

  <form id="login-form" onsubmit="handleSubmit(event)">
    <input type="text" name="email" value="Eden" />
    <input type="password" name="password" value="L@veEden" />
    <input type="submit" value="Submit">
  </form>
  
  <script>
  function handleSubmit(event) {
    event.preventDefault()
    let formElement = document.getElementById("login-form")
    let formData = ""
    formData = "email=" + formElement[0].value
    formData = formData + "&password=" + formElement[1].value
    fetch("http://localhost:8080/login", {
      method: "POST",
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: formData
    })
  }
  </script>

執行結果


修改後就能讓 Web Server 順利解析參數。

使用 URLSearchParams

URLSearchParams 是個處理各種URL查詢參數轉換的類別,在這個例子只需要將 FormData 再丟入 URLSearchParams 處理過,即能得到 application/x-www-form-urlencoded 所需的查詢參數,程式如下所示:

<html>
<head></head>
<body>
  <form id="login-form" onsubmit="handleSubmit(event)">
    <input type="text" name="email" value="Eden" />
    <input type="password" name="password" value="L@veEden" />
    <input type="submit" value="Submit">
  </form>
  
  <script>
  function handleSubmit(event) {
    event.preventDefault()
    let formData = new FormData(document.getElementById('login-form'))
    var searchParams = new URLSearchParams(formData);
    fetch("http://localhost:8080/login", {
      method: "POST",
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: searchParams
    })
  }
  </script>
</body>
</html>

執行結果


一樣修改後就能讓 Web Server 順利解析參數。


總結

新方法但沿用舊觀念,在某些前端框架看不到的細節還是要拆成香草JS小部件來多做嘗試才行。

前端修改下我會偏好使用 URLSearchParams,在開發上比較省力,程式也比較好懂。

以上提供各位參考。謝謝大家收看!


【2021.05.18 更新】

jQuery 和 form 的結合


<html>
<head>
  <script
  src="https://code.jquery.com/jquery-3.6.0.js"
  integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk="
  crossorigin="anonymous"></script>
</head>
<body>
  <form id="login-form" onsubmit="handleSubmit(event)">
    <input type="text" name="email" value="Eden" />
    <input type="password" name="password" value="L@veEden" />
    <input type="submit" value="Submit">
  </form>
  
  <script>
  function handleSubmit(event) {
    event.preventDefault()
    const formData = {user:document.getElementByName("user").value, password:document.getElementByName("password").value}
	
	// item 的內容也可以用 $.ajax.beforeSend 替換
	const item = {
		'url': "http://localhost:8080/login",
		'type': 'POST',
		'headers': {
		  'Authorization': `Basic ${ base64Auth }`,
		},
		//'contentType': false, // Default = application/x-www-form-urlencoded
		//'processData': false, // false = jq 不轉譯 Json to Parameters; Default=True
		'data': formData
	}

    $.ajax(item)
      .done(function (response) {
        console.log(response)
      })

      .fail(function (response) {
        console.log('Fail : ' + response.responseText)
      })
  }
  </script>
</body>
</html>


沒有留言:

張貼留言