没有为响应类型找到合适的HttpMessageConverter

时间:2014-02-18 12:48:45

标签: java xml spring jaxb

使用spring,使用以下代码:

List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
for(HttpMessageConverter httpMessageConverter : messageConverters){
  System.out.println(httpMessageConverter);
}
ResponseEntity<ProductList> productList = restTemplate.getForEntity(productDataUrl,ProductList.class);

我得到了

org.springframework.http.converter.ByteArrayHttpMessageConverter@34649ee4
org.springframework.http.converter.StringHttpMessageConverter@39fba59b
org.springframework.http.converter.ResourceHttpMessageConverter@383580da
org.springframework.http.converter.xml.SourceHttpMessageConverter@409e850a
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@673074aa
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@1e3b79d3
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@52bb1b26

org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.mycopmany.ProductList] and content type [text/html;charset=UTF-8]

pojo的片段:

@XmlRootElement(name="TheProductList")
public class ProductList {

@XmlElement(required = true, name = "date")
private LocalDate importDate;

10 个答案:

答案 0 :(得分:31)

从Spring的角度来看,HttpMessageConverter注册的RestTemplate个实例都无法将text/html内容转换为ProductList个对象。感兴趣的方法是HttpMessageConverter#canRead(Class, MediaType)。上述所有内容的实现都会返回false,包括Jaxb2RootElementHttpMessageConverter

由于没有HttpMessageConverter可以读取您的HTTP响应,因此处理失败并出现异常。

如果您可以控制服务器响应,请对其进行修改以将Content-type设置为application/xmltext/xml或匹配application/*+xml的内容。

如果您不控制服务器响应,则需要编写并注册自己的HttpMessageConverter(可以扩展Spring类,请参阅AbstractXmlHttpMessageConverter及其子类)并转换text/html

答案 1 :(得分:9)

如果您无法更改服务器媒体类型响应,则可以扩展GsonHttpMessageConverter以处理其他支持类型

public class MyGsonHttpMessageConverter extends GsonHttpMessageConverter {
    public MyGsonHttpMessageConverter() {
        List<MediaType> types = Arrays.asList(
                new MediaType("text", "html", DEFAULT_CHARSET),
                new MediaType("application", "json", DEFAULT_CHARSET),
                new MediaType("application", "*+json", DEFAULT_CHARSET)
        );
        super.setSupportedMediaTypes(types);
    }
}

答案 2 :(得分:7)

如果您使用的是Spring Boot,则可能需要确保在类路径中具有Jackson依赖项。您可以通过以下方式手动执行此操作:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

或者您可以使用网络启动器:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

答案 3 :(得分:4)

你可以组成一个RestTemplateXML类,它扩展了RestTemplate。然后覆盖doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>),并明确获取response-headers并将content-type设置为application/xml

现在Spring读取头文件并知道它是`application / xml'。它有点像黑客但它有效。

public class RestTemplateXML extends RestTemplate {

  @Override
  protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
        ResponseExtractor<T> responseExtractor) throws RestClientException {

     logger.info( RestTemplateXML.class.getSuperclass().getSimpleName() + ".doExecute() is overridden");

     Assert.notNull(url, "'url' must not be null");
     Assert.notNull(method, "'method' must not be null");
     ClientHttpResponse response = null;
     try {
        ClientHttpRequest request = createRequest(url, method);
        if (requestCallback != null) {
           requestCallback.doWithRequest(request);
        }
        response = request.execute();

        // Set ContentType to XML
        response.getHeaders().setContentType(MediaType.APPLICATION_XML);

        if (!getErrorHandler().hasError(response)) {
           logResponseStatus(method, url, response);
        }
        else {
           handleResponseError(method, url, response);
        }
        if (responseExtractor != null) {
           return responseExtractor.extractData(response);
        }
        else {
           return null;
        }
     }
     catch (IOException ex) {
        throw new ResourceAccessException("I/O error on " + method.name() +
              " request for \"" + url + "\":" + ex.getMessage(), ex);
     }
     finally {
        if (response != null) {
           response.close();
        }
     }

  }

  private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
     if (logger.isDebugEnabled()) {
        try {
           logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
                 response.getRawStatusCode() + " (" + response.getStatusText() + ")");
        }
        catch (IOException e) {
           // ignore
        }
     }
  }

  private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
     if (logger.isWarnEnabled()) {
        try {
           logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
                 response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
        }
        catch (IOException e) {
           // ignore
        }
     }
     getErrorHandler().handleError(response);
  }
}

答案 4 :(得分:1)

除了所有答案之外,如果您碰巧收到text/html而您预期其他内容(即application/json),则可能表明服务器端发生了错误(说404)并且返回了错误页面而不是您的数据。

所以它发生在我的情况下。希望它能节省一些人的时间。

答案 5 :(得分:1)

您还可以简单地告诉您的MapView.Animated接受所有媒体类型:

RestTemplate

答案 6 :(得分:0)

或者您可以使用

  

public void setSupportedMediaTypes(List supportedMediaTypes)

属于AbstractHttpMessageConverter<T>的方法,添加一些您喜欢的ContentTypes。这种方式可以让MappingJackson2HttpMessageConverter canRead()响应,并将其转换为您想要的类,在本例中,它是ProductList类。

我认为这一步应该与Spring Context初始化相关联。例如,通过使用

  

实现ApplicationListener {   ...   }

答案 7 :(得分:0)

试试这个:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.6.0</version>
</dependency>

答案 8 :(得分:0)

Vadim Zin4uk's answer的细化只是使用现有的GsonHttpMessageConverter类,但是调用setSupportedMediaTypes()setter。

对于Spring启动应用程序,这会导致向配置类添加以下内容:

private void loadListView(String response,float lowerLimit,float upperLimit)
{
    //Log.e("test", "loadList:"+response);
    if(Globals.geoJsonResponse.size() == 0)
    {
        //Log.e("test", "aaa:"+Globals.geoJsonResponse.size());
        try {
            JSONObject featureCollection=new JSONObject(response);
            try{
                FirstProjectApplication.mSearchSubTitle =  featureCollection.getString("subTitle");
                getActivity().getActionBar().setSubtitle(FirstProjectApplication.mSearchSubTitle);
            }catch(Exception e){}
            globalResponseObject.clear();
            popupResponseObject.clear();
            JSONArray features=featureCollection.getJSONArray("features");
            for (int i = 0; i < features.length(); i++) {

                JSONObject properties=features.getJSONObject(i);
                String type = properties.getJSONObject("properties").getString("type");
                String author = properties.getJSONObject("properties").getString("author");
                float mag=Float.parseFloat(properties.getJSONObject("properties").getString("mag"));


                SimpleDateFormat  format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
                Date date1 = format.parse(properties.getJSONObject("properties").getString("time"));

                GeoJsonResponse obj=new GeoJsonResponse(
                        properties.getJSONObject("properties").getString("eventId"),
                        properties.getJSONObject("properties").getString("author"),
                        properties.getJSONObject("properties").getString("place"),
                        Double.parseDouble(properties.getJSONObject("properties").getString("mag")),
                        Double.parseDouble(properties.getJSONObject("geometry").getJSONArray("coordinates").getString(2)),
                        properties.getJSONObject("properties").getString("time"),date1,
                        Double.parseDouble(properties.getJSONObject("geometry").getJSONArray("coordinates").getString(1)),  //lat,lon is reversed
                        Double.parseDouble(properties.getJSONObject("geometry").getJSONArray("coordinates").getString(0))


                );

                if(author.equals("AUTOMATIC")) {
                    popupResponseObject.add(obj);
                } else {
                    if((mag>=lowerLimit&&mag<upperLimit)) {
                        globalResponseObject.add(obj);
                        //Log.e("test", "mag:"+mag);
                    }
                    Globals.geoJsonResponse.add(obj);
                }
            }
            //  Collections.sort(globalResponseObject, new DateSorter());
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            //Log.e("test", e.getLocalizedMessage());
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            //Log.e("test", e.getLocalizedMessage());
        }

        sort();
        adpater=new  CustomListAdapter(getActivity(), globalResponseObject);
        geoJSON.setAdapter(adpater);
        //Log.e("test", "bbb:"+globalResponseObject.size());
    } else {
        //Log.e("test", "bbb:"+globalResponseObject.size());
        globalResponseObject.clear();
        for(GeoJsonResponse geo_response:Globals.geoJsonResponse)
        {
            if((geo_response.getMagnitude()>=lowerLimit&&geo_response.getMagnitude()<upperLimit)) globalResponseObject.add(geo_response);
        }
        sort();
        adpater.notifyDataSetChanged();
    }

    if(globalResponseObject.size() == 0) {
        new AlertDialog.Builder(getActivity(),R.style.AppCompatAlertDialogStyle)
                .setTitle(getString(R.string.allert))
                .setMessage(getString(R.string.allertsms ))
                .setPositiveButton("Ok",null)
                .show();


    }
}

答案 9 :(得分:-1)

这并没有回答这个问题,但是如果有人遇到这个问题时遇到了这个没有找到合适的消息转换器的例外,这就是我的问题和解决方案。

在Spring 4.0.9中,我们能够发送此

    JSONObject jsonCredential = new JSONObject();
    jsonCredential.put(APPLICATION_CREDENTIALS, data);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

ResponseEntity<String> res = restTemplate.exchange(myRestUrl), HttpMethod.POST,request, String.class);

在Spring 4.3.5版本中,我们开始看到错误消息,找不到转换器。 COnverets工作的方式是,如果你在类路径中有它,它们会被注册.jackson-asl仍然在classpath中但是没有被spring识别。我们用更快的xml jackson核心取代Jackson-asl。 一旦我们添加,我可以看到转换器已注册enter image description here