Jacksonと一緒のSpring JSON-P

1概要

Web上で何かを開発しているのであれば、AJAXリクエストを処理する際にブラウザが持つ 同一生成元ポリシーの制約 を知っているでしょう。制約の簡単な概要は、異なるドメイン、スキーマ、またはポートからの要求は許可されないということです。

JSONデータを扱うときに このブラウザの制限を緩和 する方法の1つは、パディング付きのJSONを使用することです(https://en.wikipedia.org/wiki/JSONP[JSON-P])。

この記事では、 AbstractJsonpResponseBodyAdvice を使って、JSON-Pデータを扱うためのSpringのサポートについて説明します。

2 JSON-Pの実行中

<script> タグに同一生成元ポリシーが適用されていないため、スクリプトを異なるドメインにロードすることができます。 JSON-P技術は、JavaScript関数の引数としてJSON応答を渡すことによってこれを利用します。

2.1. 準備

この例では、この単純な Company クラスを使用します。

public class Company {

    private long id;
    private String name;

   //standard setters and getters
}

このクラスはリクエストパラメータをバインドし、JSON表現としてサーバーから返されます。

Controllerメソッドも同様に単純な実装です - Company インスタンスを返します:

@RestController
public class CompanyController {

    @RequestMapping(value = "/companyRest",
      produces = MediaType.APPLICATION__JSON__VALUE)
    public Company getCompanyRest() {
        Company company = new Company(1, "Xpto");
        return company;
    }
}

クライアントサイドでは、 jQuery ライブラリを使ってAJAXリクエストを作成し送信することができます。

$.ajax({
    url: 'http://localhost:8080/spring-mvc-java/companyRest',
    data: {
        format: 'json'
    },
    type: 'GET',
    ...
});

次のURLに対するAJAXリクエストを考えます。

http://localhost:8080/spring-mvc-java/companyRest

サーバーからの応答は次のようになります。

{"id":1,"name":"Xpto"}

リクエストが同じスキーマ、ドメイン、およびポートに対して送信されたため、レスポンスはブロックされず、JSONデータはブラウザによって許可されます。

2.2. クロスオリジンリクエスト

リクエストURLを次のように変更します。

http://127.0.0.1:8080/spring-mvc-java/companyRest

localhost から 127.0.0.1 に送信された要求により、応答はブラウザによってブロックされます。これは別のドメインと見なされ、同一生成元ポリシーに違反しています。

JSON-Pでは、リクエストにコールバックパラメータを追加することができます。

http://127.1.1.1:8080/spring-mvc-java/companyRest?callback=getCompanyData

クライアントサイドでは、AJAXリクエストに次のパラメータを追加するのと同じくらい簡単です。

$.ajax({
    ...
    jsonpCallback:'getCompanyData',
    dataType: 'jsonp',
    ...
});

getCompanyData は、応答を受け取ったときに呼び出される関数になります。

サーバーが次のように応答をフォーマットするとします。

getCompanyData({"id":1,"name":"Xpto"});

要求と応答の両方で getCompanyData が一致するため、クライアントとサーバーの間でネゴシエートされ合意されたスクリプトとして応答を処理するため、ブラウザはそれをブロックしません。

3 @ ControllerAdvice アノテーション

@ ControllerAdvice のアノテーションが付けられたBeanは、コントローラのすべてまたは特定のサブセットを支援することができ、異なるコントローラ間で共有される横断的な動作をカプセル化するために使用されます。一般的な使用パターンは、 モデルへの属性の追加 またはバインダーの登録に関連しています。

  • Spring 4.1 ** 以降、 @ ControllerAdvice ResponseBodyAdvice インタフェースの実装を登録することができます。これにより、コントローラメソッドによって返された後、適切なコンバータによって書き込まれる前に応答を変更できます。

4 AbstractJsonpResponseBodyAdvice を使用したレスポンスの変更

  • また、Spring 4.1 ** からは、 AbstractJsonpResponseBodyAdvice クラスにアクセスできるようになりました。このクラスは、JSON-P標準に従って応答をフォーマットします。

このセクションでは、既存のコントローラを変更せずに基本クラスを有効にしてレスポンスを変更する方法について説明します。

JSON-PのSpringサポートを有効にするために、設定から始めましょう:

@ControllerAdvice
public class JsonpControllerAdvice
  extends AbstractJsonpResponseBodyAdvice {

    public JsonpControllerAdvice() {
        super("callback");
    }
}

サポートは AbstractJsonpResponseBodyAdvice クラスを使用して行われます。

スーパーメソッドに渡されるキーは、JSON-Pデータを要求するURLで使用されるものです。

このコントローラのアドバイスにより、レスポンスをJSON-Pに自動的に変換します。

** 5実際にSpringを使ったJSON-P

**

これまでに説明した設定が整っていれば、RESTアプリケーションにJSON-Pで応答させることができます。次の例では、会社のデータを返すので、AJAXリクエストURLは次のようになります。

http://127.0.0.1:8080/spring-mvc-java/companyRest?callback=getCompanyData

前の構成の結果として、応答は次のようになります。

getCompanyData({"id":1,"name":"Xpto"});

説明したように、この形式の応答は異なるドメインから発信されたにもかかわらずブロックされません。

JsonpControllerAdvice は、 @ ResponseBody および ResponseEntity という注釈が付けられた応答を返すメソッドに簡単に適用できます。

すべての応答を処理するために、コールバックで渡される同じ名前の関数 getCompanyData が必要です。

6. 結論

この簡単な記事では、JSON-Pを利用するために応答をフォーマットするという面倒な作業が、Spring 4.1の新機能を使用してどのように簡略化されるかを示します。

例とコードスニペットの実装はこのhttps://github.com/eugenp/tutorials/tree/master/spring-mvc-java#readme[GitHubプロジェクト]にあります。