0

多くの作業の後、Spring-RS、Spring MVC、Spring コントローラー、および Spring フレームワーク内のこれらのコントローラー ユーザー Jackson を利用して、応答を JSON に変換する既存のバックエンド Web サービス アプリケーションができました。

これは WEB-INF/myproject-servlet.xml の一部です

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
          <property name="objectMapper">
             <bean class="com.fasterxml.jackson.databind.ObjectMapper">

                     <property name="dateFormat">
                        <bean class="java.text.SimpleDateFormat">
                            <constructor-arg type="java.lang.String" value="yyyy-MM-dd"></constructor-arg>
                        </bean>
                     </property>
             </bean>
          </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <property name="supportedMediaTypes" value="application/json"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="messageConverters">
      <list>
        <ref bean="jsonHttpMessageConverter" />
      </list>
  </property>
</bean>

この Web サービス アプリはうまく機能します。WAR をローカルの tomcat にデプロイでき、問題なくデプロイされます。コントローラーの単体テストを行って、URL が正しく、Web アプリが Spring 内で正しく構成されていることを確認できます。URL にアクセスして、期待どおりに JSON データを取得できます。URL は次のとおりです。

http://mylocalhost/myproject/invoices/invoiceId/1

1 つの請求書を返します。

現在、無料バージョンの SmartGWT Web アプリを実行しており、RestDataScource コントローラーを持っています。以前に多くの SmartGWT Web アプリを作成しましたが、これらのアプリはエンティティ、dao、サービス レイヤー、コントローラー、およびデータソースをすべて含んでいました。これにより、コントローラーとデータソースが同じアプリ内にある場合、クライアント間の問題はまったく発生しませんでした。そして、私はそれを再び行うことに反対していませんが、それらを分離しようとしています.

私は最近、これが機能しないことを見ました!!! 開発モードの Jetty 内でローカルに実行されている SmartGWT Web アプリを使用します。開始 URL は次のとおりです。

     http://mylocalhost:8888/myapp

そして、これがバックエンドを呼び出そうとすると

    http://mylocalhost:8080/my-ws, then my listgrid gives me a warning message.

1行だけ追加できるとしたら: RPCManager.setAllowCrossDomainCalls(true); これを RESTDataSource 内に追加しますか? これをどこに追加しますか?そして、それは本当にすべてを機能させるのでしょうか? 他に追加する必要があるものはありますか?

そのため、XJSONDataSource を調べていたところ、RestDataSource を XJsonDataSource に変換するには、RestDataSource にいくつかの変更を加える必要があることがわかりました。別の投稿でここにいくつかの素晴らしい情報があり、追加することを提案しました:

   // Where do I put this line?   the controller or the datasource
   String callbackString = request.getParameter("callback");

   // Where do I put this code?  the controller or the datasource
   response.setContentType("text/X-JSON");
   response.getWriter().write( callbackString + " ( " + JSONstring + " ) " );
   response.setStatus(HttpServletResponse.SC_OK);  

このコードがどこにあるのかわからないので、追加のヘルプが必要です。コントローラーに関する限り、次のようになります。

    @RequestMapping(value = "/invoiceId", method = RequestMethod.GET, headers = "Accept=application/json")
    public @ResponseBody
        InvoiceDetailDTO getContactTypeByUserId(@RequestBody String invoiceNumber)
     {
         InvoiceDetailDTO invoiceDetailDto = invoiceService.getInvoiceDetail(invoiceNumber);

        // invoiceDetailDto is automatically converted to JSON by Spring
        return invoiceDetailDto;
     }

上記のコードでは、「リクエスト」と「レスポンス」をコントローラーに入力する必要がありますが、どうすればよいですか?

最終的には、RestDataSource を使用して希望どおりに機能するように微調整し、これらのクロスサイトの問題を無視したいと考えています。XJSONDataSource を使用する必要がある場合は、本当に良い例と、必要に応じてコントローラーを微調整する方法の例が必要です。

ありがとう!

4

1 に答える 1

1

RPCManager.setAllowCrossDomainCalls(true);初期化の初期段階で呼び出す必要があります (eg- onModuleLoad())。

getContactTypeByUserIdAccess-Control-Allow-Origin適切な値を持つ応答ヘッダーとして追加する必要がある場合があります。http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
を 確認してください。http://forums.smartclient.com/showthread.php?t=15487に 基づくと、SmartGWT はクロスドメイン リクエストを単独で処理する必要があります。

最悪のシナリオとして、これを機能させるために必要なヘッダーとともに JSONP スタイルの応答を送信する必要がある場合があります。
その場合、SmartGWT 要求を処理するために、次のような別の方法を用意するのがおそらく最善です。
私は XJSONDataSource を扱ったことがないので、以下は単なるガイドラインです。

// use a slightly different URI to distinguish from other controller method
@RequestMapping(value = "/invoiceId/sgwt", method = RequestMethod.GET, headers = "Accept=application/json")
public @ResponseBody String getContactTypeByUserIdForSgwt(@RequestBody String invoiceNumber,
        HttpServletRequest request, HttpServletResponse response) {

     // can reuse normal controller method
     InvoiceDetailDTO invoiceDetailDto = getContactTypeByUserId(invoiceNumber);

     // use jackson or other tool to convert invoiceDetailDto to a JSON string
     String JSONstring = convertToJson(invoiceDetailDto);

    // will have to check SmartGWT request to make sure actual parameter name that send the callback name
    String callbackString = request.getParameter("callback"); 

    response.setContentType("text/X-JSON");

    return  callbackString + " ( " + JSONstring + " ) " ;
 }

アップデート

以前の作業の残り物のため、コードをクリーンアップする (または最初から/最小から開始する) ことをお勧めします。

これを解決するには、次の 3 つのフェーズがあります。 1. サービスを使用せず
に SmartGWT を正しく動作させる 2. サービスを CORS 要求で正しく動作させる 3. SmartGWT を切り替えてサービスを使用する

フェーズ 1 は、クライアント側の問題を解決するために使用する必要があります。
クライアントが同じホスト/ドメインにデプロイされたときにサービスを使用している場合は、フェーズ 2 にスキップします。

フェーズ 1
このために、RestDataSource JSON 応答で説明されているように、静的応答を提供するデータ URL を使用できます。
のようなファイルにサンプル応答を配置し、test.jsonクライアント Web アプリケーションからアクセスできるようにします。
DataSource コードを最小限に抑え、場所とともに使用setDataURL();test.jsonます。

test.json- フィールド名と値を変更 (必要に応じて追加)

{    
 response:{
    status:0,
    startRow:0,
    endRow:3,
    totalRows:3,
    data:[
        {field1:"value", field2:"value"},
        {field1:"value", field2:"value"},
        {field1:"value", field2:"value"},
    ]
 }
}

情報源

public class TestDS extends RestDataSource {

    private static TestDS instance = new TestDS();

    public static TestDS getInstance() {
        return instance;
    }

    private TestDS() {
        setDataURL("data/test.json");       // => http://<client-app-host:port>/<context>/data/test.json
        setDataFormat(DSDataFormat.JSON);
        // setClientOnly(true);

        DataSourceTextField field1 = new DataSourceTextField("field1", "Field 1");
        DataSourceTextField field2 = new DataSourceTextField("field2", "Field 2");

        setFields(field1, field2);
    }
}

フェーズ 2
詳細については、参考文献を確認してください。

でホストされているページおよび でホストされているサービスから作成された、失敗したプリフライト CORS リクエストのヘッダー。 ポートが異なるため失敗しました。異なるスキーム (https/ftp/file/etc.) または異なるホスト/ドメインでも失敗します。localhost:8118localhost:7117

Host: localhost:7117
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:8118                   <= indicates origin to which access should be granted
Access-Control-Request-Method: GET              <= indicates the method that will be used in actual request
Access-Control-Request-Headers: content-type    <= indicates the headers that will be used in actual request

Server: Apache-Coyote/1.1
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Content-Length: 0

成功したリクエストのリクエスト/レスポンス ヘッダーのペア。

Host: localhost:7117
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:8118
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type

Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:8118
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Content-Length: 0

Host: localhost:7117
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Referer: http://localhost:8118/cors-test.html
Origin: http://localhost:8118

Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: *
Content-Type: application/json
Transfer-Encoding: chunked

CORS リクエストをサポートするために、サービス バックエンドは、サービス コールだけでなく、プリフライト OPTIONS リクエストに正しく応答する必要があります。
これは、ServletFilter を使用して実行できます。

<filter>
    <filter-name>corsfilter</filter-name>
    <filter-class>test.CorsFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>corsfilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

public class CorsFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
            response.addHeader("Access-Control-Allow-Origin", "http://localhost:8118");

            // list of allowed methods, Access-Control-Request-Method must be a subset of this
            response.addHeader("Access-Control-Allow-Methods", "GET");
            // list of allowed headers, Access-Control-Request-Headers must be a subset of this
            response.addHeader("Access-Control-Allow-Headers", "Content-Type, If-Modified-Since");

            // pre-flight request cache timeout
            // response.addHeader("Access-Control-Max-Age", "60");
        }
        filterChain.doFilter(request, response);
    }
}

@RequestMapping(method = RequestMethod.GET, value = "/values", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map> getValues() {
    List<Map<String, Object>> values = getValues(); // handle actual data processing and return a list suitable for response

    SgwtResponse sgwtResponse = new SgwtResponse(); // A POJO with basic (public) attributes
    sgwtResponse.status = 0L;
    sgwtResponse.startRow = 0L;
    sgwtResponse.endRow = Long.valueOf(values.size());
    sgwtResponse.totalRows = sgwtResponse.startRow + sgwtResponse.endRow;
    sgwtResponse.data = values; // java.util.List

    Map<String, SgwtResponse> jsonData = new HashMap<String, SgwtResponse>();
    jsonData.put("response", sgwtResponse);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Access-Control-Allow-Origin", "*"); // required

    return new ResponseEntity<Map>(jsonData, headers, HttpStatus.OK);
}

jQuery を使用して、XHR を使用して JSON 応答を取得する単純なテスト ページ。
URL を変更してクライアント Web アプリケーションにデプロイし、SmartGWT を使用せずにサービスを直接テストします。

<!DOCTYPE html>
<html>
    <head>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script>
            $(document).ready(function () {
                $("#retrieve").click(function () {
                    $.ajax({
                        type: "GET",
                        contentType: "application/json",
                        url: "<URL-of-service>",
                        dataType: "json",
                        success: function (data, status, xhr) {
                            $("#content").text(JSON.stringify(data, null, 2));
                        },
                        error: function (xhr, status, error) {
                            $("#content").text("Unable to retrieve data");
                        }
                    });
                });
            });
        </script>
    </head>
    <body>
        <input type="button" id="retrieve" value="Retrieve"/>
        <div id="content"/>
    </body>
</html>

If-Modified-SinceAccess-Control-Allow-HeadersSmartGWTにはヘッダーが必要でした。警告を回避するには、SmartGWT の初期化中に
使用します。RPCManager.setAllowCrossDomainCalls(true);

最近のほとんどのブラウザー (ブラウザー互換性1 ) と SmartGWT RestDataSourceは、CORS 要求をサポートしています。
XJSONDataSource は、ブラウザーが CORS 要求と互換性がないため、JSONP に依存する必要がある場合にのみ使用してください。

プリフライト リクエストを送信Access-Control-Allow-Origin: *すると、任意のサイトがサービスに対してクロス ドメイン呼び出しを行うことができます。これにより、セキュリティ上の問題が発生する可能性があり、さらに*特定の CORS リクエストでは使用できません。
より良いアプローチは、クロス ドメイン リクエストが許可される正確なサイトを指定することです - Access-Control-Allow-Origin: http://www.foo.com.
この場合はおそらく必要ありませんが、Access-Control-Allow-Origin Multiple Origin Domains を確認してください。必要に応じて、複数のサイトが CORS リクエストを行えるようにする方法を見つけます。

参考文献:
[1] https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
[2] http://java-success.blogspot.com/2012/11/cors-and-jquery-with-spring -mvc-restful.html

于 2013-08-16T22:07:19.707 に答える