
シェア:
シナはVonageのJavaデベロッパー・アドボケイト。アカデミックなバックグラウンドを持ち、自動車、コンピューター、プログラミング、テクノロジー、人間性など、あらゆることに好奇心旺盛。余暇には散歩をしたり、対戦型ビデオゲームをしたりしている。
SDKがREST APIに付加価値を与える方法
所要時間:2 分
Vonage Messages Vonage Messages v1 APIが最近一般提供ステータスに昇格しました。この発表を受けて、Developer Relations Tooling Teamは、私たちのサーバSDKにこのAPIのサポートを実装するために懸命に取り組んでいます。
私たちは、各SDKが最も理にかなった方法でAPIを実装すべきであり、言語、コミュニティ、そしてSDK内の他のAPIをサポートするために使用される一般的なアプローチを考慮し、一貫性を持たせることにしました。私たちのゴールは、実装が自動生成されたように感じられるような画一的なアプローチではなく、各SDKがイディオムに感じられるようにすることです。Messages v1 APIはこれを説明する良い方法です。
もちろん、チームのJavaデベロッパー・アドボケイトとして、私はJava SDKの実装に焦点を当て、その設計の根拠を説明するつもりだ。その前に メッセージAPI仕様これは私たちが実装する必要があるものです。
APIのエンドポイントは1つで、POSTリクエストでメッセージを送信する。複雑さはメッセージのスキーマから生じる。ここには2つの主要な概念がある: チャネルそして メッセージタイプ.前者は、メッセージの送信に使用されるサービス(SMS、MMS、WhatsApp、Viber、Facebook Messenger)を指します。後者は、メッセージの媒体(テキスト、画像、音声、ビデオ、ファイルなど)を表します。
各チャンネルは、メッセージタイプのサブセットをサポートしています。例えば、SMSでVideoを送信することはできません。WhatsAppのように、テンプレートやカスタムメッセージタイプをサポートするチャンネルもあります。 位置情報の共有.
さらに、各チャンネルはメッセージ内容に独自の制限を設けています。例えばWhatsAppは様々なオーディオフォーマットに対応していますが、MessengerはMP3のみ対応しています。例えば、WhatsAppは幅広いオーディオフォーマットに対応していますが、MessengerはMP3のみ対応しています。 キャプションAPI仕様ではキャプションと呼ばれる)を添付できるチャンネルもあれば、そうでないチャンネルもあります。
例えば、MMSにキャプションを含めることができますが、vCardの場合は例外です(ちなみに、これはMMSチャネルに固有のものです)。実装者の立場からすると、APIを使う人の混乱を最小限に抑えつつ、これらの要求を適切に構造化し、効率的に取り込む方法を見つけることが課題となります。
Javaの開発者として、データ・モデルから始めるのは自然なことのように思える。必然的に、私たちはメッセージを表現する抽象型に行き着く。 sendMessageメソッドによって受け入れられる。 MessagesClient.メッセージを送信して返ってくるレスポンスは、メッセージのタイプやチャンネルに関係なく同じなので、メッセージそのものを気にする必要はない。それを念頭に置いて、抽象的な MessageRequestは、すべてのメッセージに共通するフィールド、つまり、タイプ、チャネル、送信者、受信者、そしてオプションのクライアント参照文字列を定義します。チャネルとメッセージ・タイプはあらかじめ定義されているので、それらを列挙として表します。
オプションのパラメータと必須のパラメータが混在している(例えば、クライアント参照はオプションだが、送信者と受信者は必須)。チャネルとメッセージ・タイプの組み合わせによっては、さらに多くのパラメータがある。Javaには名前付き引数がないので、これをエミュレートするためにビルダー・パターンを使います。これについては こちら.
メッセージ・タイプとチャネルの有効な組み合わせだけが構築できるようにしたいので、この行列を Channel.のコンストラクタのすべての引数を検証します。 MessageRequestおよびそのサブタイプのコンストラクタのすべての引数を検証します。ここに、Java SDKのMessages API実装の設計の主な利点があると思います。 構造上正しい.
これはいったいどういうことなのか?理論的には、無効なメッセージを作成することは難しい、理想的には不可能である、ということです。これを読んでいる人に、Java SDKを使ってMessages APIを「悪用」してみることに挑戦してもらいたい。一緒にやってみましょう。
MessageRequestは抽象的なので、具象的なサブタイプのひとつを使わなければならない。これらのサブタイプはすべて(たとえば MmsImageRequest)はすでに Channelと MessageType.の独自のサブクラスを作ったとしても、MessageTypeとChannelのバリデーション・チェックを回避することはできない。 MessageRequestのコンストラクタで行われるからだ。 MessageRequest.さらに、列挙型は暗黙的に final なので、独自の MessageTypeや Channelを追加したり、バリデーションの動作をオーバーライドしたりすることはできません。
のすべてのサブクラスのコンストラクタはprotected(抽象クラスの場合)かpackage-private(具象クラスの場合)である。 MessageRequestのすべてのサブクラスのコンストラクタは(抽象クラスの場合)protected か(具象クラスの場合)package-private のどちらかです。 final(であるため、どの具象クラスも拡張できない(ビルダーも同様)。そのため、構造的に "悪い "クラスを作ることはできません。 MessageRequestクラスを作ることはできません。
では、別の方法を試してみよう。構造的に誤用できないのであれば MessageRequestを構造的に誤用できないなら、ビルダーに無効な値を渡すことができるかもしれない。例えば、空のテキストを送ることはできないだろうか?あるいは to(受信者) フィールドに、数字の代わりに意味不明な文字列を渡すのはどうだろう?あるいは、必須パラメータ(送信者や受信者など)を省略するのはどうでしょう?
ファイルベースのメッセージタイプ(イメージ、オーディオ、ビデオなど)にはすべてURLフィールドがあります。無効なURL文字列を渡すことはできないのでしょうか?あるいは、ViberメッセージのTime-to-Liveフィールドはどうでしょう?これは単なる数値なので、許容範囲外の数値を設定することができます。 intでしかないのだから、許容範囲外の数値を設定することもできるだろう?Messengerのリクエストでは Categoryと Tagはオプションです。しかし、APIでは Categoryの値が message-tagの値がある場合、タグが存在しなければならない。そして...
そこまでにしておこう。これらのケースはすべて検討済みであり、どれもあり得ない。たしかに、不正なURLや無効な数字を渡そうとしたり、ビルダーで必須パラメータを設定しなかったりしても、コードはコンパイルされます。しかし、実行時にはメッセージを送信することはできません。オブジェクトを構築することさえできません。 MessageRequestオブジェクトを構築することすらできません!なぜか?すべてのパラメータはコンストラクタで検証されるからです。あなたが build()を呼び出した瞬間にコンストラクタが呼び出されます。 IllegalArgumentExceptionが投げられます。これが、私が言った「構造上正しい」という意味です。もし MessageRequestを構築できるのであれば、私たちが判断する限り、明らかに間違っているわけではありません。
もちろん、そのメッセージが完全に有効であるとか、サーバーからエラー・レスポンスが返ってこないということではありません。例えば、存在しないがE164に準拠した電話番号にテキストを送信しようとすることもできる。この種の検証はSDKの範囲を超えており、APIが通信するバックエンド・サービスによって実行される。SDKが防御するのは、基本的に422エラーだ。では、これは何と対照的なのでしょうか?私はどこに向かっているのでしょうか?それに答えるために、正反対のcURLを見てみましょう。
APIを直接使えるのに、なぜSDKが必要なのですか?cURLを使用してViber経由で画像を送信する例を以下に示します:
これを読むのは比較的簡単ですが、このようなリクエストを手作業で書くときは、間違えやすいものです。エンドポイントのURL、HTTPメソッド、ヘッダー、認証タイプなどだけでなく、JSONフォーマットも覚えておく必要がある。引用符を見逃してしまうかもしれませんが(ほとんどすべてが文字列です)、次のことに注意してください。 ttl- は文字列ではなく整数です。 intを文字列で包んだものではないことに注意してください!すべてのフィールドとその正確な名前を覚えておく必要がある。テキストを入力するだけなので、オートコンプリートで助けてくれるIDEはここにはない。
あなたの端末はAPIについて何も知らないので、何かをタイプミスしても、リクエストを送信して422のレスポンスが返ってくるまで知る術がない。そしてそのスキーマを覚えても、直感的には理解できないだろう?タイムトゥーライブを設定したければ、それを viber_serviceオブジェクトでラップしなければならない。画像URLも同様に imageViberメッセージの場合、画像にキャプションを含めることはできません。しかし、cURLはあなたの試みを止めることはありません。APIのスキーマに準拠することはもちろん、有効なJSONである必要もありません!
つまり、cURLでMessages APIを使うには、APIリファレンスが手元に必要で、リクエストが正しいことを確認するために、本文だけでなくヘッダー(認証メソッドなど)も含めて何度も参照する必要があるということだ。エラーが発生しやすく、ミスを防いだり、意図したメッセージリクエストを作成するのに役立つものは何もない。
対照的に、Java SDKを通してMessages APIを使いたい場合は、APIリファレンスは全く必要ありません。必要なパブリック・メソッドはすべて文書化されており、何が必須で何がオプションか、使用パターン、例などが説明されている。ドキュメントを読まなくても、IDEが提供するオートコンプリートを使うだけでAPIを探索できます。たとえば、Viber の画像にキャプションを設定しようとしても、Java SDK にはその方法がないからです。つまり、ユーザーとしては、サポートされていないと結論づけるわけです。
発見力についてはどうだろう?すべての MessageRequestサブクラスはすべて [Channel][MessageType]Requestの命名スキームに従います (例えば SmsTextRequest).のすべてのサブクラスをリストするためにIDEを使うこともできます。 MessageRequest.リクエストを作成する方法はひとつだけです。 MessageRequestサブクラスに関連づけられたビルダーを通してです。その上、ビルダーのコンストラクタは public ではないので、ビルダーをインスタンス化する唯一の方法はクラスの static な builder()メソッドを呼び出すしかない。これはパッケージのプライベートコンストラクタと組み合わさって、 ビルダーを悪用することを防ぎます。
Javaのような強く型付けされた言語は、実行時だけでなくコンパイル時にも、無効な値を与えることを防ぎます。たとえば、オプションの Categoryオプショナル viber_serviceオブジェクトの有効な値がわからないかもしれませんが、Java SDKでは、コンパイラやIDEによってこれらは明らかです。 enumだからです。.
それをオブジェクトで包むというのは? viber_serviceオブジェクトでラップすることについては?そのような心配は無用で、ビルダーで必要なものを設定するだけで、SDKがそれを処理してくれる。実際、SDKはすべてのシリアライズとデシリアライズを行います。ユーザーとしては、裏側でどのシリアライゼーション・フォーマットが使われているかを知る必要も、気にする必要もない。これは、APIの代わりにSDKを使用する主な利点の1つです。 宣言的命令型ではなく 命令型ではなく宣言型である。.あなたは 何をを宣言するのであって どのようにではない。
例えば、すべてのAPIでJSONの代わりにAvroやProtobuf(あるいは、天の邪鬼なことに、古いXML)を使うことにしたとしよう。その辺に転がっているcURLスクリプトは書き換えが必要になるだろう。運良くスキーマが全く同じで、RegExの達人であれば、移行を部分的に自動化できるかもしれない。
しかし、もしスキーマを変更したらどうだろう?上の例でいくと urlパラメータをオブジェクトでラップする必要がなくなったとしたらどうだろう?あるいは viber_serviceコンテナは ttlと categoryパラメータに使われているコンテナを削除したらどうだろう?あるいは viber_serviceメッセージ・タイプを viberに変更したらどうでしょう?cURLでリクエストをハンドロールしている場合、これらすべての変更に注意する必要があります。Java SDKを使用している場合は、次のバージョンにアップグレードするだけです。 pom.xmlまたは build.gradleファイルを1文字変更するだけです。リファクタリングする必要はありません。
ここまでJava SDKに焦点を当ててきたが、強い型付けはバリデーションの前提条件ではないということは注目に値する。手作業でコーディングされたロジックとは対照的に、コンパイラーを使用することで強制が容易になるだけです。
例えば、Python SDKのMessages APIの実装は、Java SDKに比べるとサイズが小さい(100行以下の単一のファイルです!また Ruby SDKのも比較的小さい。これらのSDKはどちらも、パラメータに対して基本的な検証を行います。
同じアプローチをJava SDKでも使うことができた。パラメータを持つMapコンストラクトを受け入れ、それらを動的に検証することができたが、これはコンパイラが助けてくれないため、よりエラーになりやすい。Java SDKの実装の大きさは、Javaが冗長な言語であることによるところが大きい。 C#の実装の実装に似ているが、コード行数はかなり多い。
読者にとって、Messages v1実装の最初のPull Requestsを Java, Python, Rubyおよび PHPSDKで評価できます。
しかし、SDK設計に対するこの厳格なアプローチには欠点もある。将来のある時点で、API が Video 経由の送信をサポートしたとしましょう。cURL を使えばすぐにこれを利用できますが、メッセージ タイプとチャネルの組み合わせを検証する SDK は更新が必要になります。
また、APIが頻繁に変更される場合、検証ロジックやデータ型などをすべてこれらの変更に合わせて更新する必要があるため、SDKのメンテナにとってはメンテナンスの負担が大きくなります。しかし、いったん物事が落ち着き、APIが安定すれば(通常、私たちのAPIがベータ版からGAに移行すればそうなります)、この問題はあまり気にならなくなります。最終的に、私たちは可能な限り最高のユーザー体験を提供することを目指しています。この記事で、特注のSDKがREST APIに付加できる価値を納得していただけたなら幸いです。
私たちは常に地域社会の参加を歓迎しています。お気軽に VonageコミュニティSlackまたは ツイッター.改善や改良のご提案がある場合、またはバグを発見した場合は、ご遠慮なく GitHub.