vibe-dを使用したRESTサーバーとシンクライアント

こんにちは、Habr! アプリケーションをサーバーとクライアントに分割する場合、vibe-siteにAPIを追加する場合、または何もする必要がない場合。



これらの状況はそれほど変わらないので、最初に単純なケースを見てみましょう。





したがって、2を作成するために何をする必要があります-1つの通常のアプリケーションからの休息サーバーとシンクライアント:





退屈だが重要なポイント
まず、モデルについて少し。 執筆時点では、vibe-d-0.7.30-beta.1は関数オーバーロード(一般的に)をサポートしていませんでした。これは、部分的に論理的です。ネットワークに関する引数を渡すため、引数に関する正確な情報なしでメソッドを呼び出そうとするためですどのタイプに導かれるべきかさえ知りません-徹底的な検索で見つける必要がありますが、微妙なポイントがあります(たとえば、「5」はintとfloatの両方に持ち込むことができます)。



さらに、メソッドの引数と戻りデータは、vibe.data.jsonを使用して直列化[de]できる必要があります。 すべての組み込みデータ型と単純な構造(プライベートフィールドなし)でこれを行うことができます。 [de]シリアル化を実装するには、2つのメソッドstatic MyType fromJson(Json data)



およびJson toJson() const



を宣言できます。これは、複雑な構造をJson型に変換するプロセスを記述します。



これは返されたインターフェイスには適用されません。ネットワーク経由で引数を渡すことでも機能しますが、別の点があります。 返されたインターフェイスオブジェクトを実装するクラスのインスタンスを返すメソッドは引数を受け入れてはなりません。 ここで1つだけ説明できます:インスタンスは残りのインターフェイスを登録するために使用され、関数が引数を受け入れる場合、init値を持つ引数を持つインスタンスを作成することはおそらく不可能ですが、何らかの方法でネストされたインターフェイスを登録するためにインスタンスを作成する必要があります。


そのため、インターフェースを選択します。



 interface IModel { @method(HTTPMethod.GET) float triangleAreaByLengths(float a, float b, float c); @method(HTTPMethod.GET) float triangleAreaByPoints(Point a, Point b, Point c); } class Model : IModel { ... }
      
      





@method(HTTPMethod.GET)



デコレータ@method(HTTPMethod.GET)



、ルーティングの構築に必要です。 それらなしで行う方法もあります-メソッドの命名規則(プレフィックス)を使用します:





サーバーコードは、モジュールの静的コンストラクターのクラシックな雰囲気で記述されます。



 shared static this() { auto router = new URLRouter; router.registerRestInterface(new Model); //     auto set = new HTTPServerSettings; set.port = 8080; set.bindAddresses = ["127.0.0.1"]; listenHTTP(set, router); }
      
      





そして最後に、モデルを使用したコードの変更:



 ... auto m = new RestInterfaceClient!IModel("http://127.0.0.1:8080/"); //       ...
      
      





フレームワーク自体は、サーバーへの呼び出しとデータ型の[de]シリアル化を実装します。



その結果、アプリケーションをサーバーとクライアントに分割し、既存のコードを最小限に変更しました! ところで、スローされた例外は、残念なことに、例外のタイプを保存せずに 、バイブによってクライアントアプリケーションにスローされます。



より複雑なケースを考えてみましょう-モデルには、シリアル化できないオブジェクト(クラス)の配列を返すメソッドがあります。 残念ながら、既存のコードを変更せずにはできません。 この例では、この状況を認識しています。



異なるポイントアグリゲーターを返します。



 interface IPointCalculator { struct CollectionIndices { string _name; } //      @method(HTTPMethod.GET) Point calc(string _name, Point[] points...); } interface IModel { ... @method(HTTPMethod.GET) Collection!IPointCalculator calculator(); } class PointCalculator : IPointCalculator { Point calc(string _name, Point[] points...) { import std.algorithm; if (_name == "center") { auto n = points.length; float cx = points.map!"ax".sum / n; float cy = points.map!"ay".sum / n; return Point(cx, cy); } else if (_name == "left") return points.fold!((a,b)=>ax<bx?a:b); else throw new Exception("Unknown calculator '" ~ _name ~ "'"); } } class Model : IModel { PointCalculator m_pcalc; this() { m_pcalc = new PointCalculator; } ... Collection!IPointCalculator calculator() { return Collection!IPointCalculator(m_pcalc); } }
      
      





本質的に、 IPointCalculator



はコレクションの要素ではありませんが、コレクション自体とCollectionIndices



構造は、このコレクションの要素を取得するために使用されるインデックスの存在を示すだけです。 _name



前の下線は、 calc



メソッドへの要求の形式をcalculator/:name/calc



として決定します。ここで、 :name



メソッドの最初のパラメーターとして渡され、 CollectionIndices



では、 new RestInterfaceClient!IModel



を使用してインターフェイスを実装するときにこのような要求を作成できます。



次のように使用されます。



 ... writeln(m.calculator["center"].calc(a, b, c)); ...
      
      





戻り値の型がCollection!IPointCalculator



からIPointCalculator



変更された場合、ほとんど変更されません。



 ... writeln(m.calculator.calc("center", a, b, c)); ...
      
      





この場合、リクエストの形式は同じままです。 この組み合わせにおけるCollection



の役割は完全には明らかではありません。



前菜には、クライアントのWebバージョンを実装します。 これを行うには、次のものが必要です。





vibeで使用されるダイエッ​​トテンプレートは、 jadeに非常に似ています



 html head title  REST style. .label { display: inline-block; width: 20px; } input { width: 100px; } script(src = "model.js") script. function getPoints() { var ax = parseFloat(document.getElementById('ax').value); var ay = parseFloat(document.getElementById('ay').value); var bx = parseFloat(document.getElementById('bx').value); var by = parseFloat(document.getElementById('by').value); var cx = parseFloat(document.getElementById('cx').value); var cy = parseFloat(document.getElementById('cy').value); return [{x:ax, y:ay}, {x:bx, y:by}, {x:cx, y:cy}]; } function calcTriangleArea() { var p = getPoints(); IModel.triangleAreaByPoints(p[0], p[1], p[2], function(r) { document.getElementById('area').innerHTML = r; }); } body h1    div div.label A: input#ax(placehoder="ax",value="1") input#ay(placehoder="ay",value="2") div div.label B: input#bx(placehoder="bx",value="2") input#by(placehoder="by",value="1") div div.label C: input#cx(placehoder="cx",value="0") input#cy(placehoder="cy",value="0") div button(onclick="calcTriangleArea()")  p : span#area
      
      





もちろん、まあまあですが、規範の例としては:









サーバーコードの変更:



 ... auto restset = new RestInterfaceSettings; restset.baseURL = URL("http://127.0.0.1:8080/"); router.get("/model.js", serveRestJSClient!IModel(restset)); router.get("/", staticTemplate!"index.dt"); ...
      
      





ご覧のとおり、vibeはAPIにアクセスするためのjsコードを生成します。



結論として、この段階では、たとえば、返されたすべてのインターフェイス(jsオブジェクトのこれらのフィールド)のjsコードの不正な生成、および特定のコレクション(url- :name



不正な生成)交換済み)。 しかし、これらの粗さは簡単に修正できます 。近い将来修正されると思います



それだけです! サンプルコードはgithubからダウンロードできます。



All Articles