RESTインターフェースvibe.dフレームワークジェネレーター

この記事では、vibe.dフレームワークに組み込まれたrest interface generatorの使用方法を段階的に示します。 Dとその機能に興味がある場合は、この記事で、初心者向けにDを実際に使用する方法を少し明確にし、そのパワーについてすべて学ぶことを願っています。



これを行うには、次のものが必要です。

  1. vibe.dで動作するようにEclipseをセットアップします。 新人
  2. vibe.dで「Hello World」を起動します。 新人
  3. OpenWeatherMap APIのクライアントを作成します。
  4. OpenWeatherMap APIに基づいてバックアップサーバーを作成する


vibe.dのドキュメントにはジェネレーター使用例があります。 しかし、私の意見では、この例は単純すぎて、ジェネレーターの動作メカニズムを明らかにしていません。



準備する



D言語には、個々の本格的なideと既存のプラグインの両方を含む多くのプログラミングツールがあります。 eclipseのクロスプラットフォームの性質のおかげで、同じオペレーティングシステムに接続できなくなるため、私は自分でeclipseのDDTプラグインを選択しました。

ツール
  • dmdをダウンロードしてインストールします
  • ダブをダウンロードしてインストールします。 これからは、Dでプログラムできます。ただし、ideが必要です。
  • eclipseをダウンロードしてインストールします。
  • ddtをインストールします


これで、インストールは完了したと見なすことができます。 次に、プロジェクトを設定します。 ちなみに、標準のDDT設定は「hello world」などの単純なプログラムの作成に適していますが、通常のコンソールは崇高なテキストエディタによる組み込みのDサポートは言うまでもなく、そのような目的にも理想的です。



より複雑な場合は、ダブをコレクターとして使用すると便利です。 これを行うには、標準のddtビルダーを無効にします:Project-> Properties-> Builders、そこにダブを追加し、プロジェクトとビルド引数を含むディレクトリから起動します。 また、Window-> Preferences-> DDT-> Compilersメニューにdmdコンパイラがあることを確認することをお勧めします。 そうでない場合は、そのパスを示します。 次に、dttは標準のphobosライブラリにアクセスします。





vibe.dのHello World
  • プロジェクトを初期化します。
    dub init helloVibe
          
          



  • dub.jsonを編集しましょう。 以下を追加してください



     "libs-posix": ["dl"], "versions": ["VibeCustomMain"], "dependencies": { "vibe-d": "~master" }
          
          







  • 次に、helloVibeディレクトリから
     dub run
          
          



    おめでとうございます、あなたの最初のDプログラムはうまくいきました。
  • Eclipseでプロジェクトを作成します。 そして、ライブラリとして、ソースフォルダを追加します
     %APPDATA%/dub/packages/vibe-d-master/source
          
          



    オートコンプリートは私たちのアシスタントです。
  • vibe.dを実行します。 これを行うには、ポートのリッスンを開始し、イベントループを開始する必要があります。 これは関数によって行われます
     listenHTTP(HTTPServerSettings,URLRouter)
          
          



     runEventLoop()
          
          



    それに応じて。 また、サーバー設定を作成し、ルーターを構成する必要があります。
  • 次のような設定を作成します。



     auto settings = new HTTPServerSettings; settings.bindAddresses = ["127.0.0.1"]; settings.port = 80;
          
          



  • バイブを接続することを忘れないでください:
     import vibe.d;
          
          



  • ルーターには、GETメソッドでアクセスできる1つのアドレスが含まれます



     auto router = new URLRouter; router.get("/", &index);
          
          







  • そして、関数ハンドラー:



     void index(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("Hello World!"); }
          
          







    ハンドラーは関数またはデリゲートのいずれかです。これは非常に便利で、クラスメソッドをハンドラーとして使用できます。 この機能は、RESTインターフェイスジェネレーターとWebインターフェイスジェネレーターの両方で使用されます。
  • すべてを正しい順序で呼び出すことが残っています。



     import std.stdio; import vibe.d; void main() { writeln("Edit source/app.d to start your project."); void index(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("Hello World!"); } auto settings = new HTTPServerSettings; settings.bindAddresses = ["127.0.0.1"]; settings.port = 80; auto router = new URLRouter; router.get("/", &index); listenHTTP(settings, router); runEventLoop(); }
          
          







  •  dub run
          
          



    。 おめでとうございます、vibe.dの最初のサーバーの準備ができました。






レストインターフェースジェネレーター





お客様




Vibe.dは、サーバーとクライアントの両方に適しています。

残りのインターフェイスを作成するには、インターフェイス(論理的)を宣言する必要があります。 この記事では OpenWeatherMap APIのクライアントを検討します。 このAPIには、qパラメーターを持つ1つのweatherメソッドのみがあります。 だから書く



 interface OpenWeather { Weather getWeather(string q); }
      
      







サーバーからの応答は、自動的にWeather構造に変換されます。 OpenWeatherMap APIに従って説明します

サーバー応答が変換される構造
 struct Weather { @Label("City identification") long id; @Label("Data receiving time, unix time, GMT") ulong dt; @Label("City name") string name; WCoord coord; WSys sys; WMain main; WWind wind; WClouds clouds; //@optional() //WConditions[] weather; @optional() WRain rain; @optional() WSnow snow; } struct WCoord { @Label("City geo location, lat") double lat; @Label("City geo location, lon") double lon; } struct WSys { @Label("System parameter, do not use it") double message; @Label("Country (GB, JP etc.)") string country; @Label("Sunrise time, unix, UTC") ulong sunrise; @Label("Sunset time, unix, UTC") ulong sunset; } struct WMain { @Label("Temperature, Kelvin (subtract 273.15 to convert to Celsius)") double temp; @Label("Humidity, %") double humidity; @Label("Minimum temperature at the moment. This is deviation from current temp that is possible for large cities and megalopolises geographically expanded (use these parameter optionally)") double temp_min; @Label("Maximum temperature at the moment. This is deviation from current temp that is possible for large cities and megalopolises geographically expanded (use these parameter optionally)") double temp_max; @Label("Atmospheric pressure, hPa ") double pressure; @Label("Atmospheric pressure on the sea level, hPa") @optional() double sea_level; @Label("Atmospheric pressure on the ground level, hPa") @optional() double grnd_level; } struct WWind { @Label("Wind speed, mps") double speed; @Label("Wind direction, degrees (meteorological)") double deg; @Label("Wind gust, mps") @optional() double gust; } struct WClouds { @Label("Cloudiness, %") double all; } struct WConditions { @Label("Weather condition id") long id; @Label("Group of weather parameters (Rain, Snow, Extreme etc.)") Weather main; @Label("Weather condition within the group") string description; @Label("Weather icon id") string icon; } struct WRain { @Label("Precipitation volume for last 3 hours, mm") @optional() double _3h; } struct WSnow { @Label("Snow volume for last 3 hours, mm") @optional() double _3h; }
      
      





jambs vibe.dに関するいくつかの言葉。

まず、ユーザータイプからの配列の逆シリアル化はうまく機能しません。

第二に、同様のプログラミング言語の特性により、変数は数字で始まることはできません(dだけでなく)。 私は、この点をAPI開発者が十分に考えていないので、vibe.dではなくOpenWeatherMap APIアカウントにこのカントを書き込みます。



Dにより、応答を集中管理できます。 Label ()属性(ここでは「dog」)には変数の説明が含まれ、@ optional()には、このフィールドがサーバー応答に含まれない可能性があることがジェネレーターに伝えられます。 ラベル ()はユーザー定義です。これにより、たとえば、説明と変数の対応を心配する必要がなくなります。
 Label
      
      



構造です:



 struct Label { string text; }
      
      







あとは、インターフェースクライアントを作成するだけです。 これは次のように行われます。



 auto client = new RestInterfaceClient!OpenWeather("http://api.openweathermap.org/data/2.5/");
      
      







ここで「!」 渡されたインターフェース。 これは、RestInterfaceClientテンプレートクラスがコンパイル時にそれを使用することを意味します。 Dでは、プログラムは最初にコンパイル時に実行され、次に実行時に実行されます。 コンパイル時には、テンプレートがインスタンス化されます。つまり、特別なクラスが生成され、そのオブジェクトはプログラムの実行時に使用されます。

メソッドがget *で始まる場合、ジェネレータはGETメソッドを使用してこのハンドラーを登録します。ポスト*、put *、delete *と同様です。 メソッドを指定する必要はまったくありません。そうすると、ジェネレーターがその処理方法を決定します。 詳細については、vibe.dのドキュメントを参照してください。

インデックス関数を書き換える


 void index(HTTPServerRequest req, HTTPServerResponse res) { auto client = new RestInterfaceClient!OpenWeather("http://api.openweathermap.org/data/2.5/"); auto weather = client.getWeather("Moscow"); string result = "<html>"; foreach(each; respond(weather)) { auto writer = res.bodyWriter(); if (!each.label.empty) { result ~=format("<b>%s</b>:<br/>\"%s\" = %s<br/>", each.label, each.name, each.value); } else { result ~= format("<i>%s</i><br/>", each.name); } } result ~= "</html>"; res.writeBody(result); }
      
      







応答機能


 import std.traits; import std.conv; struct A { string name; string value; string label; } string getLabel(alias ST, alias mem)() { foreach (attr; __traits(getAttributes, __traits(getMember,ST,mem))) { if (is(typeof(attr) == Label)) { return attr.text; } } return ""; } A[] respond(ST)(ST st) { A[] ret = new A[0]; foreach(mem; __traits(derivedMembers, ST)) { static if(is(typeof(__traits(getMember,ST,mem)) == struct)) { ret ~= A(mem,"",""); ret ~= respond!(typeof(__traits(getMember,ST,mem)))(__traits(getMember,st, mem)); } else { ret ~= A(mem, to!string(__traits(getMember,st, mem)), getLabel!(ST,mem) ); } } return ret; }
      
      







getLabelテンプレートは、説明を変数に復元します(変数と説明の対応については上で書きました)。 応答テンプレートは、変数名、その値、および説明をA構造の便利な配列に収集します



プロジェクトを組み立てて開始すると、 127.0.0.1にモスクワの天気に関する詳細情報が届きます。





サーバー


サーバーを作成するには、APIを解放するクラス、つまりクラスOpenWeatherMapImplを作成できます。



 class OpenWeatherImpl:OpenWeather { Weather getWeather(string q) { auto client = new RestInterfaceClient!OpenWeather("http://api.openweathermap.org/data/2.5/"); return client.getWeather(q); } }
      
      







このクラスは、公式のOpenWeatherMapサーバーを複製します。 登録インターフェース
 router.registerRestInterface(new OpenWeatherImpl);
      
      







すべて一緒に
 import std.traits; import std.conv; struct Label { string text; } struct A { string name; string value; string label; } string getLabel(alias ST, alias mem)() { foreach (attr; __traits(getAttributes, __traits(getMember,ST,mem))) { if (is(typeof(attr) == Label)) { return attr.text; } } return ""; } A[] respond(ST)(ST st) { A[] ret = new A[0]; foreach(mem; __traits(derivedMembers, ST)) { static if(is(typeof(__traits(getMember,ST,mem)) == struct)) { ret ~= A(mem,"",""); ret ~= respond!(typeof(__traits(getMember,ST,mem)))(__traits(getMember,st, mem)); } else { ret ~= A(mem, to!string(__traits(getMember,st, mem)), getLabel!(ST,mem) ); } } return ret; } struct Weather { @Label("City identification") long id; @Label("Data receiving time, unix time, GMT") ulong dt; @Label("City name") string name; WCoord coord; WSys sys; WMain main; WWind wind; WClouds clouds; //@optional() //WConditions[] weather; @optional() WRain rain; @optional() WSnow snow; } struct WCoord { @Label("City geo location, lat") double lat; @Label("City geo location, lon") double lon; } struct WSys { @Label("System parameter, do not use it") double message; @Label("Country (GB, JP etc.)") string country; @Label("Sunrise time, unix, UTC") ulong sunrise; @Label("Sunset time, unix, UTC") ulong sunset; } struct WMain { @Label("Temperature, Kelvin (subtract 273.15 to convert to Celsius)") double temp; @Label("Humidity, %") double humidity; @Label("Minimum temperature at the moment. This is deviation from current temp that is possible for large cities and megalopolises geographically expanded (use these parameter optionally)") double temp_min; @Label("Maximum temperature at the moment. This is deviation from current temp that is possible for large cities and megalopolises geographically expanded (use these parameter optionally)") double temp_max; @Label("Atmospheric pressure, hPa ") double pressure; @Label("Atmospheric pressure on the sea level, hPa") @optional() double sea_level; @Label("Atmospheric pressure on the ground level, hPa") @optional() double grnd_level; } struct WWind { @Label("Wind speed, mps") double speed; @Label("Wind direction, degrees (meteorological)") double deg; @Label("Wind gust, mps") @optional() double gust; } struct WClouds { @Label("Cloudiness, %") double all; } struct WConditions { @Label("Weather condition id") long id; @Label("Group of weather parameters (Rain, Snow, Extreme etc.)") Weather main; @Label("Weather condition within the group") string description; @Label("Weather icon id") string icon; } struct WRain { @Label("Precipitation volume for last 3 hours, mm") @optional() double _3h; } struct WSnow { @Label("Snow volume for last 3 hours, mm") @optional() double _3h; } interface OpenWeather { Weather getWeather(string q); } class OpenWeatherImpl:OpenWeather { Weather getWeather(string q) { auto client = new RestInterfaceClient!OpenWeather("http://api.openweathermap.org/data/2.5/"); return client.getWeather(q); } } import std.stdio; import vibe.d; import std.string; void main() { writeln("Edit source/app.d to start your project."); void index(HTTPServerRequest req, HTTPServerResponse res) { auto client = new RestInterfaceClient!OpenWeather("http://api.openweathermap.org/data/2.5/"); auto weather = client.getWeather("Moscow"); string result = "<html>"; foreach(each; respond(weather)) { auto writer = res.bodyWriter(); if (!each.label.empty) { result ~=format("<b>%s</b>:<br/>\"%s\" = %s<br/>", each.label, each.name, each.value); } else { result ~= format("<i>%s</i><br/>", each.name); } } result ~= "</html>"; res.writeBody(result); } auto settings = new HTTPServerSettings; settings.bindAddresses = ["127.0.0.1"]; settings.port = 80; auto router = new URLRouter; router.get("/", &index); router.registerRestInterface(new OpenWeatherImpl); listenHTTP(settings, router); runEventLoop(); }
      
      









アドレス127.0.0.1/weather?q= "Moscow"で、モスクワの天気に関するjsonの回答を受け取ります。



便利な形式の記事の例github.com/ntstv/viberest



All Articles