🛠️

[Node] HTML form から express に patch / delete リクエストを送る

2022/08/30に公開

半日ほどググりながら悶えていたので書き残してみます。間違っている部分があればご指摘ください🙏

HTML の from から put, delete のリクエストを送信しようとしたところ、何度やっても post リクエストになってしまうという問題が発生しました。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="style.css" />
    <script src="script.js" defer></script>
    <title>Form Submit Demo</title>
  </head>
  <body>
    <h1>Form Submit Demo</h1>
    <section id="data"></section>
    <form method="delete" action="/">
      <input type="submit" value="データを消す" />
    </form>
  </body>
</html>
require("dotenv").config({ path: "./.env.local" });

const express = require("express");
const methodOverride = require("method-override");
const path = require("path");

const knex = require("knex");
const config = require("../knexfile");
const db = knex(config);

const PORT = process.env.PORT || 9000;

const app = express();

app.use(express.json());
app.use(express.static(path.join(__dirname, "..", "public")));

app.get("/", (req, res) => {
  res.sendFile(path.resolve(__dirname, "..", "index.html"));
});

app.delete("/", (req, res) => {
  console.log("accept delete request");
  db("data").del();
  res.sendFile(path.resolve(__dirname, "..", "index.html"));
});

app.get("/api/data", async (req, res) => {
  const data = await db("data").select();
  res.json(data);
});

app.listen(PORT, async () => {
  await db.migrate.latest();
  await db.seed.run();
  console.log(`app listening on port ${PORT}`);
});

調べてみるとバグではなく仕様。form はデフォルトで GET または POST のリクエストしか送ることができないそうです。
https://stackoverflow.com/questions/5162960/should-put-and-delete-be-used-in-forms
https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-method

fetch を使うといいらしいことはわかったのですが、今回は express でサーバーを立てていたこともあり、express の method-override を使って put / delete リクエストを送ってみました。

インストール

method-override - npm

npm install method-override
または
yarn add method-override

method-override 以外の使用技術

  • backend: express, knex, dotenv
  • database: postgres
  • package manager: yarn

使い方

method-override の他に、テキストデータを受け取るために express のビルトインミドルウェアである express.urlencode を仕様する必要があります。

require("dotenv").config({ path: "./.env.local" });

const express = require("express");
// method-override を require
const methodOverride = require("method-override"); 
const path = require("path");

const knex = require("knex");
const config = require("../knexfile");
const db = knex(config);

const PORT = process.env.PORT || 9000;

const app = express();

app.use(express.json());
// extended: true と設定することで配列も読み込めるようになります。false でもOK
app.use(express.urlencoded({ extended: true }));

// "_method" で指定されたクエリを HTTP リクエストとして扱います
app.use(methodOverride("_method"));

app.use(express.static(path.join(__dirname, "..", "public")));

app.get("/", (req, res) => {
  res.sendFile(path.resolve(__dirname, "..", "index.html"));
});

app.delete("/", async (req, res) => {
  console.log("accept delete request");
  await db("data").del();
  res.sendFile(path.resolve(__dirname, "..", "index.html"));
});

(省略)

そして HTML も少し編集する必要があります。form タグの action にクエリとして HTTP メソッドを足してあげましょう。

<form method="post" action="/?_method=DELETE">
  <input type="submit" value="データを消す" />
</form>

method="delete"method=post として解釈されるため、分かりやすいように post に直しました。実際に HTTP メソッドとして method-override に判断されているのは、 action 属性に与えたクエリ部分です。上の例では、パス / のクエリとして method=DELETE を与えています。

同様に patch も実装することができます。

<h2>データを編集する</h2>
<form class="flex" method="post" action="/?_method=PATCH">
  <section>
    <label for="name">どのidのデータを編集しますか?</label>
    <input type="number" name="id" />
  </section>
  <section>
    <label for="name">内容を入力してください。</label>
    <input type="text" name="contents" />
  </section>
</form>
app.patch("/", async (req, res) => {
  console.log("accept patch request");
  const { id, contents } = req.body;
  await db("data").where({ id: id }).del();
  await db("data").insert([{ id, contents }]);
  res.sendFile(path.resolve(__dirname, "..", "index.html"));
});

今回は該当のデータを一旦消して変更 & insert し直すという処理をしているので、patch した要素が一番下に表示されてしまいます。

HTMLを見ると一番下に表示されている

気になる場合はGETリクエストの時に順番を並び替える処理をしてあげてください。

app.get("/api/data", async (req, res) => {
  const data = await db("data").select().orderBy("id");
  res.json(data);
});

リンク

記事内で使用したコードは Github にもアップロードしています。

公式ページ

https://www.npmjs.com/package/method-override

参考にした記事🙏

https://expressjs.com/en/api.html#express.urlencoded
https://reffect.co.jp/node-js/first-time-express-js

Discussion