[Node] HTML form から express に patch / delete リクエストを送る
半日ほどググりながら悶えていたので書き残してみます。間違っている部分があればご指摘ください🙏
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 のリクエストしか送ることができないそうです。
fetch を使うといいらしいことはわかったのですが、今回は express でサーバーを立てていたこともあり、express の method-override を使って put / delete リクエストを送ってみました。
インストール
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 した要素が一番下に表示されてしまいます。
気になる場合はGETリクエストの時に順番を並び替える処理をしてあげてください。
app.get("/api/data", async (req, res) => {
const data = await db("data").select().orderBy("id");
res.json(data);
});
リンク
記事内で使用したコードは Github にもアップロードしています。
公式ページ
参考にした記事🙏
Discussion