49

したがって、クライアントがHATEOASの原則に従うことを可能にする表現を提供する RESTful サービスを実装する必要があるということを、今では理解しています。理論的にはすべて理にかなっていますが、Web を精査して、アイデアに厳密に従うクライアント コードの良い例を 1 つ見つけました。

読めば読むほど、誰も実際にやっているわけではないので、これは学術的な議論のように感じ始めています! 人々は WS-* スタックの多くの欠陥について好き勝手に不満を言うことができますが、少なくともクライアントを作成する方法は明らかです。つまり、WSDL を解析してコードを生成できます。

これは、優れた RESTful サービスでは必要ないことを理解しています。関連する関係と表現について知る必要があるだけで、それらに動的に反応できる必要があります。それでも、この原則は、今までにいくつかの一般的なライブラリに抽出され、抽象化されているべきではありませんか? 受け取る可能性のある表現と関係に関する情報をフィードして、アプリケーションで使用できるより有用な高レベルのコードを取得しますか?

これらは私の中途半端なアイデアに過ぎませんが、もし私が飛び込んで適切な RESTful API を今すぐ書いたとしても、実際には誰もそれを使用できなくなるのではないかと心配しています! または、少なくともそれを使用することは、私が提供する関係と表現を解釈するために人々がグルーコードを書かなければならない余分なマイルのために、背後で非常に苦痛になるでしょう.

クライアントの観点から、これに光を当てることができる人はいますか? 私が実際に書いている聴衆のアイデアを得ることができるように、誰かが適切に動的/反応的なRESTfulクライアントコードの例を示すことができますか? (さらに、いくつかの抽象化を提供するクライアント API の例) それ以外の場合は、すべてかなり理論的です....

[編集: 注、私はここで同様の質問を見つけましたが、実際には回答されていないと思います。著者はウィキペディアのスタブを手に入れました!]

4

6 に答える 6

18

現在のプロジェクトでは、これを半分やりました。返される表現はドメイン オブジェクトから生成され、クライアントはそれらを XML、JSON、または XHTML で要求できます。Firefox のような XHTML クライアントの場合、既知のルート リソースから一連のアウトバウンド リンクが表示され、他のすべてのリソースをブラウズできます。これまでのところ、純粋な HATEOAS であり、開発者にとって優れたツールです。

しかし、クライアントがプログラムであり、ブラウザを使用する人間ではない場合、パフォーマンスが懸念されます。XML および JSON 表現については、現在、関連するリンクの生成を抑制しています。これは、表現サイズが 3 倍になり、シリアライゼーション/デシリアライゼーション、メモリ使用量、および帯域幅に大幅に影響するためです。効率に関するもう 1 つの懸念事項は、純粋な HATEOAS では、クライアント プログラムがよく知られているリンクから必要な情報を参照するときに、数倍の数の HTTP 要求を行うことです。したがって、クライアントがエンコードされたリンクの知識を持っている場合、効率の観点からは最善のようです。

ただし、これを行うと、クライアントは URI を形成するために多くの文字列を連結する必要があり、エラーが発生しやすく、リソースの名前空間を再配置するのが難しくなります。したがって、クライアント コードがテンプレートを選択し、パラメーター オブジェクトからそれ自体を展開するように要求するテンプレート システムを使用します。これはフォーム入力の一種です。

他の人がこれについて経験したことを知りたいと思っています。HATEOAS は、パフォーマンスの側面を除けば、良いアイデアのように思えます。

編集: 私たちのテンプレートは、 Restletフレームワークの上に書いた Java クライアント ライブラリの一部です。クライアント ライブラリは、HTTP 要求/応答、HTTP ヘッダー、デシリアライゼーション/シリアライゼーション、GZIP エンコーディングなどのすべての詳細を処理します。これにより、実際のクライアント コードが非常に簡潔になり、サーバー側の変更から隔離されます。

HATEOAS に関するRoy Fielding のブログ エントリには、それに続く非常に関連性のある興味深い議論があります。

于 2009-11-21T18:09:00.510 に答える
10

これまでに、RESTサービスにアクセスする2つのクライアントを構築しました。どちらもHATEOASを排他的に使用します。クライアントを更新せずにサーバー機能を更新できることで、私は大きな成功を収めました。

xml:baseを使用して相対URLを有効にし、xmlドキュメントのノイズを減らします。画像やその他の静的データの読み込みを除いて、私は通常、ユーザーリクエストのリンクのみをたどるので、リンクのパフォーマンスのオーバーヘッドは私にとって重要ではありません。

クライアントでは、作成する必要があると感じた唯一の一般的な機能は、メディアタイプのラッパーとリンクを管理するためのクラスです。


アップデート:

クライアントの観点から、RESTインターフェースを処理する2つの異なる方法があるようです。1つ目は、クライアントが取得したい情報と、その情報に到達するためにトラバースする必要のあるリンクを知っている場所です。2番目のアプローチは、どのリンクをたどるかを制御するクライアントアプリケーションの人間のユーザーがいて、クライアントがサーバーから返されるメディアタイプを事前に知らない場合に役立ちます。娯楽的価値のために、私はこれらの2つのタイプのクライアントをそれぞれデータマイナーとディスパッチャーと呼びます。

データマイナー

たとえば、Twitter APIが実際にRESTfulであり、特定のTwitterユーザーの最新のフォロワーの最新のステータスメッセージを取得するクライアントを作成したいと考えてみてください。

すばらしい新しいMicrosoft.Http.HttpClientライブラリを使用していて、Twitter APIからのXMLを解析するための「ReadAs」拡張メソッドをいくつか作成したとすると、次のようになります。

var twitterService = HttpClient.Get("http://api.twitter.com").Content.ReadAsTwitterService();

var userLink = twitterService.GetUserLink("DarrelMiller");
var userPage = HttpClient.Get(userLink).Content.ReadAsTwitterUserPage();

var followersLink = userPage.GetFollowersLink();
var followersPage = HttpClient.Get(followersLink).Content.ReadAsFollowersPage();
var followerUserName = followersPage.FirstFollower.UserName;

var followerUserLink = twitterService.GetUserLink(followerUserName);
var followerUserPage = HttpClient.Get(followerUserLink).Content.ReadAsTwitterUserPage();

var followerStatuses = HttpClient.Get(followerUserPage.GetStatusesLink()).Content.ReadAsTwitterUserPage();

var statusMessage = followerStatuses.LastMessage; 

ディスパッチャー

この例をわかりやすく説明するために、系図情報をレンダリングするクライアントを実装していると想像してください。クライアントは、ツリーを表示し、特定の人物に関する情報にドリルダウンし、関連する画像を表示できる必要があります。次のコードスニペットについて考えてみます。

 void ProcessResponse(HttpResponseMessage response) {
            IResponseController controller;

            switch(response.Content.ContentType) {
                case "vnd.MyCompany.FamilyTree+xml":
                    controller = new FamilyTreeController(response);
                    controller.Execute();
                    break;
                case "vnd.MyCompany.PersonProfile+xml":
                    controller = new PersonProfileController(response);
                    controller.Execute();
                    break;
                case "image/jpeg":
                    controller = new ImageController(response);
                    controller.Execute();
                    break;
            }

        }

クライアントアプリケーションは、完全に汎用的なメカニズムを使用してリンクをたどり、このディスパッチ方法に応答を渡すことができます。ここから、switchステートメントは、メディアタイプに基づいて情報を解釈およびレンダリングする方法を知っている特定のコントローラークラスに制御を渡します。

明らかに、クライアントアプリケーションにはさらに多くの要素がありますが、これらはHATEOASに対応するものです。私は多くの詳細をざっと読んだので、どんな点でも明確にするように私に遠慮なく尋ねてください。

于 2009-11-22T00:13:26.430 に答える
3

Nokia のPlaces API (Web アーカイブスナップショット) は RESTful であり、全体でハイパーメディアを使用します。また、そのドキュメントでは、URI テンプレート/ハードコーディングの使用を思いとどまらせることも明らかです。

ハイパーメディア リンクの使用

アプリケーションは、URI テンプレートを使用して次のステップの URI を構築しようとするのではなく、タスク フロー内の後続の要求に対して JSON 応答で公開されているハイパーメディア リンクを使用する必要があります。URI テンプレートを使用すると、次の要求の応答を作成するために必要な重要な情報が要求に含まれなくなります。

于 2012-10-31T19:15:21.947 に答える
1

ジム、私はHATEOASに続くRESTfulクライアントの例がないことに少し不満を感じていたので、注文を作成して発注するための適切なHATEOASの例を示すブログ投稿を書きました。APIを介してこれを行う例は驚くほど少なく、少し紛らわしいと思いましたが、ここにリンクがあります: Restを使用したAPIの例。あなたの考えと私が間違ったと思うことを教えてください。

于 2012-05-16T20:57:36.743 に答える
0

私は 2 つの HATEOAS クライアントを作成しました。1 つは Java で、もう 1 つは Ruby で作成しました。あなたのフラストレーションを共有します。どちらの場合も、私が行っていたことに対するツールのサポートが完全に欠如していました。たとえば、私が使用していた REST API は、各ハイパーテキスト コントロールに使用する HTTP メソッドを教えてくれますが、HttpClientではメソッドを渡すことができないため、次の醜いコードになってしまいました (ところで、すべてのコードは内部に存在します)。カスタム Ant タスク、したがってBuildExceptions):

private HttpMethod getHypermediaControl(Node href, Node method,
        NodeList children) {
    if (href == null) {
        return null;
    }
    HttpMethod control;
    if (method == null || method.getNodeValue().equals("")
            || method.getNodeValue().equalsIgnoreCase("GET")) {
        control = new GetMethod(href.getNodeValue());
    } else if (method.getNodeValue().equalsIgnoreCase("POST")) {
        control = new PostMethod(href.getNodeValue());
    } else if (method.getNodeValue().equalsIgnoreCase("PUT")) {
        control = new PutMethod(href.getNodeValue());
    } else if (method.getNodeValue().equalsIgnoreCase("DELETE")) {
        control = new DeleteMethod(href.getNodeValue());
    } else {
        throw new BuildException("Unknown/Unimplemented method "
                + method.getNodeValue());
    }
    control.addRequestHeader(accept);
    return control;
}

これが、私が使用する REST クライアント ユーティリティ メソッドの基礎となりました。

private HttpMethod getHypermediaControl(String path, Document source)
        throws TransformerException, IOException {

    Node node = XPathAPI.selectSingleNode(source, path);
    return getHypermediaControl(node);
}

private HttpMethod getHypermediaControl(Node node) {
    if (node == null) {
        return null;
    }
    NamedNodeMap attributes = node.getAttributes();
    if (attributes == null) {
        return null;
    }
    Node href = attributes.getNamedItem("href");
    Node method = attributes.getNamedItem("method");
    HttpMethod control = getHypermediaControl(href, method,
            node.getChildNodes());
    return control;
}

private Document invokeHypermediaControl(HttpClient client, Document node,
        final String path) throws TransformerException, IOException,
        HttpException, URIException, SAXException,
        ParserConfigurationException, FactoryConfigurationError {
    HttpMethod method = getHypermediaControl(path, node);
    if (method == null) {
        throw new BuildException("Unable to find hypermedia controls for "
                + path);
    }
    int status = client.executeMethod(method);

    if (status != HttpStatus.SC_OK) {
        log(method.getStatusLine().toString(), Project.MSG_ERR);
        log(method.getResponseBodyAsString(), Project.MSG_ERR);
        throw new BuildException("Unexpected status code ("
                + method.getStatusCode() + ") from " + method.getURI());
    }
    String strResp = method.getResponseBodyAsString();
    StringReader reader = new StringReader(strResp);
    Document resp = getBuilder().parse(new InputSource(reader));
    Node rval = XPathAPI.selectSingleNode(resp, "/");
    if (rval == null) {
        log(method.getStatusLine().toString(), Project.MSG_ERR);
        log(method.getResponseBodyAsString(), Project.MSG_ERR);
        throw new BuildException("Could not handle response");
    }
    method.releaseConnection();
    return resp;
}

この少しのコードで、返されるドキュメント内のハイパーメディア コントロールをトラバースするクライアントをかなり簡単に作成できます。欠落している主なビットは、フォーム パラメーターのサポートです。幸いなことに、私が使用しているすべてのコントロールは、1 つを除いてパラメーターがありません (リファクタリングに関しては 3 つのルールに従います)。完全を期すために、そのコード スニペットは次のようになります。

    HttpMethod licenseUpdateMethod = getHypermediaControl(
            "/license/update", licenseNode);
    if (licenseUpdateMethod == null) {
        log(getStringFromDoc(licenseNode), Project.MSG_ERR);
        throw new BuildException(
                "Unable to find hypermedia controls to get the test suites or install the license");
    } else if (license != null) {
        EntityEnclosingMethod eem = (EntityEnclosingMethod) licenseUpdateMethod;
        Part[] parts = { new StringPart("license", this.license) };
        eem.setRequestEntity(new MultipartRequestEntity(parts, eem
                .getParams()));
        int status2 = client.executeMethod(eem);
        if (status2 != HttpStatus.SC_OK) {
            log(eem.getStatusLine().toString(), Project.MSG_ERR);
            log(eem.getResponseBodyAsString(), Project.MSG_ERR);
            throw new BuildException("Unexpected status code ("
                    + eem.getStatusCode() + ") from " + eem.getURI());
        }
        eem.releaseConnection();
    }

ここで、 の子を調べて、/license/updateどのパラメータを渡す必要があるかを判断する必要がありますが、従う必要があるパラメータ化されたフォームがさらに 2 つになるまで待つ必要があります

ところで、すべての努力の結果、クライアントに影響を与えずにサーバーを変更することは非常に満足のいくものであり、簡単でした. とても気持ちが良かったので、一部の州では禁止されていないことに驚きました.

于 2012-10-31T20:27:42.717 に答える
-6

選択した Web ブラウザは、WWW 全体に対する「純粋な HATEOAS」クライアントです。

質問は本当に意味がありません。

于 2009-11-23T21:21:39.757 に答える