📝

Freshで検索バーに入力した文字列でリダイレクトする方法

2023/06/04に公開

概要

DenoのwebフレームワークであるFreshを触っている中で、ユーザー名で検索する機能を実装しようとしました。ネットに公式以外だとあまり情報がなかったので、苦労しました。

Freshとは

https://fresh.deno.dev/

実装イメージ

https://sample.com
がメインページだとしたら、下記のような検索バーに入力し、送信すると、

https://sample.com/{username}
に移動するというものです。

FreshでのRouting

Freshでは/routes配下のフォルダ、ファイルの階層に従って表示します。
ex) https://sample.com/testのように表示したい場合、下記のようなイメージ

--routes
  --test.tsx

また、[name].tsx
のようにすると、その配下のページの任意の名前の場合表示されます。
今回はこの機能を使って各ユーザー名のページを表示します
https://sample.com/{name※任意の文字列}

実施過程

カスタムハンドラー

https://fresh.deno.dev/docs/getting-started/custom-handlers
各種GET, POSTが実施されたときにリクエスト、レスポンス形式で実施する機能

これを使ってform属性で送信されたデータに基づいてページ遷移を発生させることにしました。
(これがベストプラクティスかはわかりません。が公式機能を使うべきかと思いました。)

GET, POSTはページに対してリクエストがあったときに処理されます。
フォームではpostで処理するので、今回はPOST側の関数をいじることになるというわけです。

index.tsx
export const handler: Handlers = {
  async GET(_req, ctx) {
    return ctx.render();
  },
  async POST(req, ctx) {
  return ctx.render();
  },
};

フォームの実装

フォームを作成します。
フォームを作らずユーザー検索する場合などは、個別にactionを定義するなどすれば良いと思います。

index.tsx
<form method="POST">
  <input
    class="p-3 rounded-full bg-gray-100 text-center"
    type="text"
    id="summoner-name"
    placeholder="summoner name"
    name="summonerName"
  ></input>
</form>

最初に作った関数

index.tsx
export const handler: Handlers = {
  async GET(_req, ctx) {
    return ctx.render();
  },
  async POST(req, ctx) {
    const formData = await req.formData();
    const jsonData = Object.fromEntries(formData);
    # 
    const path = "/" + jsonData.name
    return new Response(null, {
      status: 307, // See Other
      headers: { Location: path},
    });
  },
};

/hogeなど一応特定条件では動作しますが、日本語名を入れるとURL処理で405エラーになります。
おそらくフロントをやる方なら常識なのだと思いますが、気づきませんでした。
修正方法は単純でencodeURIComponent(name)にすれば大丈夫です。

実際にこれで実装できたと思いましたが、正しく表示されないという挙動になりました。

しかし、ページをリロードすると何故か正しく動作しました。
405のエラーになる理由を調べてみたところ、フォームをPOSTで実装したため、Fresh側の該当ページで、POSTを許容する必要があることがわかりました。
参考:
https://developer.mozilla.org/ja/docs/Web/HTTP/Status/405

詳細は割愛しますが、[name].tsx ではGETの関数しか用意していないことがわかりました。
このURLに関しては、GETもPOSTも受け入れる方式にしたいので、今のGET側の定義をPOST側にもコピーすることで一旦対応しました。

[user].tsx

export const handler: Handlers<SummonerData | null> = {
  async GET(_, ctx) {
    // ここがデフォルトの処理
  },
  async POST(req, ctx) {
  // ↑の実装内容をここにコピー
  },

};

これで無事表示まで行きました。
Fresh+Deno deployで作るのは個人開発の構成として非常によい開発体験ですが、情報が少ないことがネックなので、もっと多くの方が使っていくと嬉しいです。

実装についてアドバイスやご指摘あればコメントいただけると助かります。

Discussion