2018年1月17日水曜日

サーバ側で JSON 情報がほしい場合にクエリストリングで JSON を送信させるのはどうなのだろうか

概要

例えばサーバ側で {"array":[{"key1":"value1"},{"key2","value2"]} みたいな情報を扱い場合に、この情報をクライアントからどうやって送ってもらうかを考えます

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

クライアントに JSON を URL エンコードしてもらいそのまま送ってもらう

サーバ側のサンプルコードは以下の通りです

  • vim app.rb
require 'sinatra'
require 'json'

get '/' do
  begin
    ret = JSON.parse(params['json'])
    "parsed json #{ret}"
  rescue
    status 500
    'json parse error'
  end
end

クライアントから送信する JSON 情報を json というパラメータに含めクエリストリングで送信します
もちろん JSON 情報は URL エンコードを掛けてもらいます

  • curl 'localhost:4567?json=%7B%22array%22%3A%5B%7B%22key1%22%3A%22value1%22%7D%2C%7B%22key2%22%3A%22value2%22%7D%5D%7D'

これでサーバ側では受け取った情報をそのまま JSON として扱うことができます
サーバ側の都合にはなりますがコードもすっきりしています
ただ、当然パースできない JSON が飛んでくる可能性もあるので、その場合の rescue 処理は必須になると思います

辛いのはクライアント側になるのでユーザフレンドリな仕様ではないかなとも思います

ほしい値をパラメータ化してサーバ側で JSON を組み立てる

今回の例で言えば key1, value1, key2, value2 をクエリストリングのパラメータとしてそれぞれ送信してもらい、それをサーバ側で受け取って JSON を組み立てます
サーバ側のコードは以下のような感じになります

  • vim app.rb
require 'sinatra'
require 'json'

get '/' do
  key1 = params['key1']
  value1 = params['value1']
  key2 = params['key2']
  value2 = params['value2']
  json = %{{"array":[{"#{key1}":"#{value1}"},{"#{key2}":"#{value2}"}]}}
  ret = JSON.parse(json)
  "parsed json #{ret}"
end

そしてクライアント側から送信する情報は以下のようになります

  • curl 'localhost:4567/?key1=key1&value1=value1&key2=key2&value2=value2'

JSON に含まれるブラケットやコロンが無いのでその部分を URL エンコードする必要はなくなります
そもそもサーバ側のサンプルコードが良くないのもありますが、この場合はパラメータを取得して自分でハッシュを組み立てる感じになると思います

クライアント側からのリクエストはあらかじめ決められたパラメータに値をセットして送信する感じになるのでキレイと言えばキレイかなと思います

それぞれのメリットデメリットを考える

どちらも一長一短かなーという気はしますが一応メリットデメリットを考えます

前者は好きな JSON を自分で組み立ててサーバ側に送信することができます
つまり可変長な値の場合には前者のほうが柔軟に対応できます
一応後者でも可変長なパラメータを持つことはできますがクライアントもサーバサイドもやたら複雑になるだけかなと思うので、その場合には素直に前者を採用したほうが良いかなと思います

ただ前者の場合 URL エンコードすることが必須になります
なので URL が全体として非常に長くなる可能性があります
GET で送信できるパラメータに制限を設けているサービスだともしかしたら上限にぶつかる可能性があります
後者もパラメータが増えれば URL が長くなる可能性があるのはあります

ドキュメントという観点からも考えると後者のほうが少し有利かなと思います
前者の場合は JSON として設定できるフォーマットもちゃんとドキュメントに載せる必要があります
後者の場合は単純に key, value になるので value 側のフォーマットを細かく考える必要がありません (型などは考える必要があるかもしれませんが)
なのでドキュメントという面では前者のほうが少し手間になるかなと思いますが、やり方次第で何とでもなるのでそこまでデメリットではないかなと思います

一番良いのはリクエストボディで JSON を送信する

これは当然の考えだと思います
最近だと REST で実装されている API は多くパラメータなどはリクエストボディに含めて送信するケースがほとんどです
その場合は Content-Type: application/json にして JSON をそのまま送信すれば良いのでクライアント側もサーバ側も簡単です

ただ REST と言えど GET の場合はクエリストリングで処理するケースがほとんどなので、その場合に JSON を送信する必要がある場合は今回のケースを考える必要があるかもしれません

最後に

クエリストリングで JSON 情報を送信しなければいけない場合にどう送信するべきかを考えてみました
最近だと GraphQL など DSL というか key, value ではなくクエリをそのまま投げて検索できるような仕様もあるのでそれを使うのも手だと思います
というか代替方法はいくらでもあると思います

今回の場合はクエリストリングという仕様に縛られた場合にどうするかというケースであることを考慮して貰えればと思います

0 件のコメント:

コメントを投稿