SmartGWT RestDataSource到Spring REST控制器GET返回415错误

时间:2013-08-20 19:23:03

标签: json spring-mvc cors smartgwt same-origin-policy

这个问题一直困扰着我,我花了很多时间在这里,并试图找到正确的答案并尝试许多不同的修复。

我有一个Spring MVC Controller,在这里定义:

@RequestMapping(value = "/searchAndCount", method = RequestMethod.GET, produces = "application/json", headers =
{ "Accept=application/json", "Content-Type=application/json" }, consumes = "application/json")
public @ResponseBody
RequestResults<?> searchAndCount(@RequestBody SearchInvoiceDTO searchInvoiceDto)
{
      RequestResults<?> requestResults = invoiceApprovalService.searchAndCount(searchInvoiceDto);
    return requestResults;
}

我知道,对于大多数获取,简单的参数可以被发回,但在这种情况下,我发现将所有搜索条件放在一个对象中并将其发送更好。这就是我在做@RequestBody的原因。

根据之前的修补程序,我确保它具有接受JSON输出所需的两个标头。

JSON字符串如下所示: String s1 =“{\”userId \“:3,\”ddUserId \“:301010651,\”customerCode \“:\”QA \“,\”customerId \“:8}”; 是的,我使用了Jackson ObjectMapper工具验证此代码将从此String正确映射到Object,反之亦然。当我查看POJO时,它确实实现了Serializable,并且它确实有一个默认的构造函数。

Junit测试工作真棒并返回数据:

MockHttpServletRequestBuilder requestBuilder =
        MockMvcRequestBuilders.get("/invoices/searchAndCount").contentType(MediaType.APPLICATION_JSON)
        .content(test);

    this.mockMvc.perform(requestBuilder).andDo(print());

这确实调用了Controller,我可以从输出中看到标题是什么,我可以看到我实际上得到了真正的数据,这很棒。所以我觉得从控制器方面我无能为力。

对控制器的真正调用来自SmartGWT RestDataSource。

RequestMethod在数据源中定义,这是init方法:

private InvoiceDataSource(String id)
{
    setID(id);
    setClientOnly(false);

    // set up FETCH to use GET requests
    OperationBinding fetch = new OperationBinding();
    fetch.setOperationType(DSOperationType.FETCH);
    fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
    fetch.setDataFormat(DSDataFormat.JSON);
    DSRequest fetchProps = new DSRequest();
    fetchProps.setHttpMethod("GET");
    fetch.setRequestProperties(fetchProps);

    // set up ADD to use POST requests
    OperationBinding add = new OperationBinding();
    add.setOperationType(DSOperationType.ADD);
    add.setDataProtocol(DSProtocol.POSTMESSAGE);
    // ===========================================
    DSRequest addProps = new DSRequest();
    addProps.setHttpMethod("POST");
    add.setRequestProperties(addProps);

    // set up UPDATE to use PUT
    OperationBinding update = new OperationBinding();
    update.setOperationType(DSOperationType.UPDATE);
    update.setDataProtocol(DSProtocol.POSTMESSAGE);
    // ===========================================
    DSRequest updateProps = new DSRequest();
    updateProps.setHttpMethod("PUT");
    // updateProps.setContentType("application/json");
    update.setRequestProperties(updateProps);

    // set up REMOVE to use DELETE
    OperationBinding remove = new OperationBinding();
    remove.setOperationType(DSOperationType.REMOVE);
    DSRequest removeProps = new DSRequest();
    removeProps.setHttpMethod("DELETE");
    remove.setRequestProperties(removeProps);

    // apply all the operational bindings
    setOperationBindings(fetch, add, update, remove);

    init();
}

Fetch设置为POSTMESSAGE,这似乎是使用transformReponse传递数据的最佳方式。

@Override
protected Object transformRequest(DSRequest dsRequest)
{
    // gets the correct URL - (web-app-root)/rest/invoices/searchAndCount
    postProcessTransform(dsRequest);

    System.out.println("InvoiceDataSource: transformRequest: START");
    dsRequest.setContentType("application/json");
    JavaScriptObject jso = dsRequest.getData();

    // setting more headers, but this doesn't seem to change anything
    dsRequest.setAttribute("Access-Control-Allow-Origin", "*");
    dsRequest.setAttribute("Content-Type", "application/json");
    dsRequest.setAttribute("Accept", "application/json");

    String s1 = JSON.encode(jso);
    System.out.println("InvoiceDataSource: transformRequest: FINISH: s1=" + s1);
    return s1;
}

因为我知道我有正确的URL,我也知道我正在吐出变量“s1”也有正确的JSON数据,我再次测试了JSON以确保它能正确地击中控制器。 / p>

我也确实在pom.xml文件中定义了Jackson的依赖项。我还在springmvc-servlet.xml文件中设置了消息转换器。如果这些不正确,则单元测试不起作用。但是,如果您需要查看pom.xml文件或springmvc-servlet.xml文件,请告诉我。

我一整天都在研究和尝试很多事情,到目前为止......没有运气。 我希望我提供了足够的信息,但如果您需要更多信息,请告诉我。 最后,我希望我可以调整我的SmartGWT RestDataSource,将正确的数据传递给该控制器,以实际获取数据。

更新: 当我在Eclipse中使用Jetty运行它时,我使用Firefox 23.0.1打开我的web应用程序。 在Eclipse中的控制台中,我可以看到以下内容:

[WARN] 415 - GET /rest/invoices/searchAndCount (127.0.0.1) 1440 bytes
Request headers
  Host: 127.0.0.1:8888
  User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.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
  Cookie: GLog=%7B%0D%20%20%20%20left%3A22%2C%20%0D%20%20%20%20top%3A11%2C%20%0D%20%20%20%20width%3A705%2C%20%0D%20%20%20%20height%3A855%2C%20%0D%20%20%20%20priorityDefaults%3A%7B%0D%20%20%20%20%20%20%20%20Log%3A4%0D%20%20%20%20%7D%2C%20%0D%20%20%20%20defaultPriority%3A3%2C%20%0D%20%20%20%20trackRPC%3Atrue%0D%7D
   Connection: keep-alive
   If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
 Response headers
  Content-Type: text/html; charset=iso-8859-1
  Content-Length: 1440
  Accept: application/json

请注意Request标头: 接受:text / html,application / xhtml + xml,application / xml; q = 0.9, / ; q = 0.8 没有显示application / json 此外,请求标头:“Content-Type”不存在

当我使用Chrome时,结果是:

[WARN] 415 - GET /rest/invoices/searchAndCount (127.0.0.1) 1440 bytes
Request headers
   Host: 127.0.0.1:8888
   Connection: keep-alive
   Accept: */*
   User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36
   DNT: 1
   Accept-Encoding: gzip,deflate,sdch
   Accept-Language: en-US,en;q=0.8
   If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
   Cache-Control: max-age=0
Response headers
   Content-Type: text/html; charset=iso-8859-1
   Content-Length: 1440
   Accept: application/json

当我从JUnit测试运行时,总会出现“Content-Type:application / json”。所以,似乎虽然我告诉SmartGWT RestDataSource我在几个地方使用JSON ......但是web服务调用没有创建正确的标题。

更新: 我将以下代码添加到SmartGWT RestDataSource transformRequest方法中:

    Map<String, String> httpHeaders = new HashMap<String, String>();
    httpHeaders.put("Accept", "*/*");
    httpHeaders.put("Content-Type", "application/json");
    dsRequest.setHttpHeaders(httpHeaders);

我可以添加“接受”请求标头,但我仍然收到415 Unsupported Media错误消息。 当我添加“Content-Type”请求标头时,我收到400 BAD REQUEST错误消息。

我现在在控制台中得到这个:

[WARN] 400 - GET /rest/invoices/searchAndCount (127.0.0.1) 1418 bytes
Request headers
   Host: 127.0.0.1:8888
   User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0
   Accept: */*
   Accept-Language: en-US,en;q=0.5
   Accept-Encoding: gzip, deflate
   Connection: keep-alive
   If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
   Content-Type: application/json
Response headers
   Content-Type: text/html; charset=iso-8859-1
   Content-Length: 1418

1 个答案:

答案 0 :(得分:2)

这需要做很多工作,但我终于明白了。

动词GET不能满足我的需要......我是个白痴。没有多少工作可以使我的参数作为请求数据中的对象发送。因此,让我展示一下我必须更改的代码才能使其正常工作。

SmartGWT RestDataSource,我改变了; fetchProps.setHttpMethod( “GET”); to fetchProps.setHttpMethod(“POST”); 我仍然保留:fetch.setDataProtocol(DSProtocol.POSTMESSAGE);

    // set up FETCH to use GET requests
    OperationBinding fetch = new OperationBinding();
    fetch.setOperationType(DSOperationType.FETCH);
    fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
    fetch.setDataFormat(DSDataFormat.JSON);
    // ===========================================
    DSRequest fetchProps = new DSRequest();
    fetchProps.setHttpMethod("POST");
    fetchProps.setContentType("application/json");
    fetch.setRequestProperties(fetchProps);

同样在同一个RestDataSource中,在“transformRequest”方法中,我添加了:

    Map<String, String> httpHeaders = new HashMap<String, String>();
    httpHeaders.put("Content-Type", "application/json");
    httpHeaders.put("Accept", "application/json");
    dsRequest.setHttpHeaders(httpHeaders);

这确保无论我使用什么浏览器,都会手动设置这两个标头。

Spring MVC Controller驻留在同一个web-app中,所以现在,这可以避免任何SOP跨站点域问题,直到我可以测试它。与此同时,我的控件的标题如下所示:

@RequestMapping(value = "/searchAndCount", method = RequestMethod.POST, headers =
{ "Accept=application/json", "Content-Type=application/json" })
public @ResponseBody
ArrayList<?> searchAndCountPOST(@RequestBody SearchInvoiceDTO searchInvoiceDto)

这项工作非常适合被调用,它返回了我的数据。我原来的单元测试是在做一个MockMVc.get,它可以通过我工作。它必须具有已识别的数据,并将GET更改为POST以使其工作。我的单元测试现在改为POST,这也很有效。

我希望所有这些工作都能为别人带来回报!