4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Deno] Web標準Responseオブジェクトで様々なデータをやり取りする

Last updated at Posted at 2023-05-12

Web標準にはHTTP通信のリクエスト/レスポンスを表すRequest / Responseオブジェクトが存在します。
これらはそれぞれfetch()関数の引数と返り値であり、フロントエンドで主に利用されています。加えて最近はDenoやNode.jsなどのサーバーサイドでもこれらのオブジェクトが活用されており、クライアント・サーバー間で様々な形式のデータを送受信することが可能になっています。

当記事ではResponseオブジェクトを使用したデータの受け渡しを、Denoを用いて解説していきます。

Responseオブジェクトを使用してデータを受け渡しする

当記事では解説のためにDeno標準ライブラリのHTTPサーバーを利用します。

このサーバーは、HTTPサーバーを「Requestオブジェクトを受け取ってResponseオブジェクトを返す関数」として書くことができます。

サーバー側
import { serve } from "https://deno.land/std@0.187.0/http/mod.ts";

serve(() => new Response());
//     ^ Requestオブジェクトを受け取ってResponseオブジェクトを返す関数

クライアント側 (ブラウザを想定) では、fetch()関数を使用してHTTPリクエストを送信します。fetch関数の返り値も、Responseオブジェクトです。

クライアント側
// 変数resにはResponseオブジェクトが入る
const res = await fetch("http://example.com");

ここからは、Deno標準ライブラリのHTTPサーバーとfetch関数を使用して、サーバーからクライアント側に様々な形式のデータを送信する方法について見ていきます。

文字列の受け渡し

文字列データを受け渡しするには、サーバー側でResponseコンストラクタの引数に文字列を入れクライアント側でawait res.text()で値を取り出せばよいです。

サーバー側
import { serve } from "https://deno.land/std@0.187.0/http/mod.ts";

serve(() => new Response("text data"));
クライアント側
// res は Response オブジェクト
const res = await fetch("http://example.com/");
const text = await res.text(); // res.text()で、サーバー側で入れた値を取り出し
console.log(text); // => "text data"

JSONデータの受け渡し

Responseの作成には、JSONデータからResponseオブジェクトを作成するためのResponse.json()メソッドが用意されています。これを利用してJSONデータのレスポンスを作成できます。

クライアント側では、await res.json()でJSONデータを取り出すことができます。

サーバー側
import { serve } from "https://deno.land/std@0.187.0/http/mod.ts";

// Response.json()を使用すると自動でContent-Typeがapplicatin/jsonに設定される
serve(() => Response.json({ key: "value" }));
クライアント側
// res は Response オブジェクト
const res = await fetch("http://example.com/");
const json = await res.json(); // res.json()で、サーバー側で入れた値を取り出し
console.log(json); // => { key: "value" }

.json()メソッドが2つ出てくるのでややこしいですが、

  • Response.json() (staticメソッド) はResponse作成用
  • Response.prototype.json() (インスタンスメソッド) はResponseからデータを取り出す用

なので混同しないようにしましょう。

Uint8Array / ArrayBufferの受け渡し

バイナリデータを送受信する際に役立つのがUint8ArrayArrayBufferです。
Denoにおいては、例えばDeno.readFile()を使ってファイルの内容をUint8Arrayで取得することができます。

これらのオブジェクトは文字列と同様、Responseコンストラクタの第1引数に与えることができます。
また、クライアント側ではres.arrayBuffer()メソッドを使用して値を取り出すことができます。

サーバー側
import { serve } from "https://deno.land/std@0.187.0/http/mod.ts";

const u8 = new Uint8Array([0, 1, 2])
serve(() => new Response(u8, {
  headers: { "Content-Type": "image/png" }, // Content-Typeはデータに合わせて適切なものを設定
}));
クライアント側
// res は Response オブジェクト
const res = await fetch("http://example.com/");
const arrayBuffer = await res.arrayBuffer(); // res.arrayBuffer()で、サーバー側で入れた値を取り出し
console.log(arrayBuffer); // => ArrayBufferオブジェクト

// ArrayBufferはUint8Arrayに変換できる
const u8 = new Uint8Array(arrayBuffer);
console.log(u8); // => Uint8Array([0, 1, 2])

なおResponseにContent-Typeを設定する際は、コンストラクタの第2引数にheadersオブジェクトを設定します。

ReadableStreamの受け渡し

Responseオブジェクトでは、Uint8Arrayが流れてくるReadableStreamを送受信できます。(TypeScriptで言うとReadableStream<Uint8Array>型)

これらは主に、ファイルの内容をストリーミングする際に使われます。
大きなファイルの場合、ReadableStreamを使用することでメモリ効率が良くなるため、データの送受信を高速化できるというメリットがあります。

Denoにおいては、(await Deno.open("file.txt")).readableのような形で、ファイルの内容を読み出すReadableStreamを取り出すことができます。

サーバー側
import { serve } from "https://deno.land/std@0.187.0/http/mod.ts";

serve(async () => {
  const readableStream = (await Deno.open("path/to/file.txt")).readable
  return new Response(readableStream);
});
クライアント側
// res は Response オブジェクト
const res = await fetch("http://example.com/");

// res.bodyの中にReadableStreamが入っている
if (res.body) {
  // ReadableStreamを読み出してconsole.logで出力
  const reader = res.body.getReader();
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    console.log(value);
  }
}

その他のデータ形式

Responseオブジェクトは上記で紹介した以外にも、以下のデータ形式に対応しています。

  • FormDataオブジェクト
  • Blobオブジェクト
  • DataViewオブジェクト
  • URLSearchParamsオブジェクト

各形式のオブジェクトについて、Response作成&データ読み出し方法をまとめたものが以下の表になります。

形式 Responseオブジェクトの作成 データの読み出し
文字列 new Response("文字列") await response.text()
JSONデータ Response.json(jsonデータ) await response.json()
Uint8Array new Response(new Uint8Array()) new Uint8Array(await response.arrayBuffer())
ArrayBuffer new Response(new ArrayBuffer()) await response.arrayBuffer()
ReadableStream new Response(new ReadableStream()) response.body
FormData new Response(new FormData()) await response.formData()
Blob new Response(new Blob()) await response.blob()
DataView new Response(new DataView()) new DataView(await response.arrayBuffer())
URLSearchParams new Response(new URLSearchParams()) new URLSearchParams(await response.text())

データ形式は相互に変換できる

HTTP通信では最終的にバイナリデータにエンコードされて送受信が行われます。Responseオブジェクトは、そのエンコードとデコードを担当しています。

つまり、

  • ReadableStreamから作成したResponseを文字列として取り出し
  • 文字列から作成したResponseをJSONデータとして取り出し
  • Uint8Arrayから作成したResponseをBlobとして取り出し

のように、データ形式を相互に変換することが可能です。

Requestオブジェクトでも同様のことが可能

上ではResponseオブジェクトを使用してサーバー側からクライアント側へデータを送信する方法について解説しました。

Web標準APIには、Responseオブジェクトだけでなく、Requestオブジェクトも存在しています。

このRequestオブジェクトを使用すると、POSTメソッドによるクライアント側からサーバー側へのデータ送信についても、ほぼ同様にデータをやり取りすることができます。

クライアント側
fetch("http://example.com/", {
  method: "POST",
  body: "文字列", // 第2引数のbodyに送信するデータを指定する。
  // body: new Uint8Array(),
  // body: new FormData(),
  // ...etc
});
サーバー側
import { serve } from "https://deno.land/std@0.187.0/http/mod.ts";

serve(async (req) => {
  const data = await req.text(); // 文字列としてで読み取り
  const data = await req.json(); // JSONデータとして読み取り
  const data = await req.arrayBuffer(); // バイナリデータとして読み取り
  const data = await req.formdata(); // Formdata形式で読み取り
  // ...etc

  return new Response("OK");
});

まとめ

  • Responseオブジェクトを使用して、文字列、JSONデータ、バイナリデータなどを送受信することができる。
  • Responseオブジェクトは、JavaScriptのオブジェクトをHTTP通信に流すバイナリデータにエンコード/デコードする役割を担当している。
  • Requestオブジェクト:クライアント側→サーバー側のデータ送信に使用
  • Responseオブジェクト:サーバー側→クライアント側のデータ送信に使用

なお、Responseオブジェクトがデータのデコード/エンコードを行うことを生かして、データ形式の変換に利用するというTipsが存在します。

// ReadableStreamをすべて読み取ったうえで文字列にデコードする関数
async function readableStreamToString(readableStream: ReadableStream<Uint8Array>) {
  // 一旦Responseオブジェクトを経由することでデータ形式を変換できる
  return await new Response(readableStream).text();
}

このように、ResponseオブジェクトはHTTP通信が関係ない場面でも活用されることがあります。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?