Could not write request: no suitable HttpMessageConverter found for request type [org.json.JSONObject] and content type [application/json]

后端 未结 5 2110
青春惊慌失措
青春惊慌失措 2020-12-18 21:16

I\'m digging myself in trying to send a POST request with a JSON payload to a remote server.

This GET curl command works fine:

curl -H \"Accept:appli         


        
相关标签:
5条回答
  • 2020-12-18 21:47

    Spring's RestTemplate does not know how to serialize the Android org.json.JSONObject class. (org.json.JSONObject is not available in "normal"/desktop Java)

    RestTemplate for Android supports

    Jackson JSON Processor, Jackson 2.x, and Google Gson.

    per: https://docs.spring.io/autorepo/docs/spring-android/1.0.1.RELEASE/reference/html/rest-template.html

    With Jackson on the classpath RestTemplate should understand how to serialize Java beans.

    CredentialsResource cr = new CredentialsResource();
    cr.setEmail(...);
    cr.setPassword(...);
    
    HttpEntity<CredentialsResource> entityCredentials = new HttpEntity<CredentialsResource>(cr, httpHeaders);
    

    P.S. It would be pretty easy to write your own JSONObjectHttpMessageConverter if you wanted to carry on using JSONObject, your converter should just have to call .toString()

    0 讨论(0)
  • 2020-12-18 21:49

    The Exception "no suitable HTTPMessageConverter found" is thrown, if not JSON implementation is present in the class path. Have a look at RestTemplate source code:

    public RestTemplate() {
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        this.messageConverters.add(new ResourceHttpMessageConverter());
        this.messageConverters.add(new SourceHttpMessageConverter<Source>());
        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    
        if (romePresent) {
            this.messageConverters.add(new AtomFeedHttpMessageConverter());
            this.messageConverters.add(new RssChannelHttpMessageConverter());
        }
    
        if (jackson2XmlPresent) {
            this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
        }
        else if (jaxb2Present) {
            this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
        }
    
        if (jackson2Present) {
            this.messageConverters.add(new MappingJackson2HttpMessageConverter());
        }
        else if (gsonPresent) {
            this.messageConverters.add(new GsonHttpMessageConverter());
        }
    }
    

    Add a JSON implementation to your class path. E. g.:

    implementation "com.fasterxml.jackson.core:jackson-databind:2.9.0"
    
    0 讨论(0)
  • 2020-12-18 21:58

    I had to do a few things to get this working.

    First I had to convert the JSONObject to a string, as in:

    HttpEntity<String> entityCredentials = new HttpEntity<String>(jsonCredentials.toString(), httpHeaders);
    

    The reason being that there is no mapping message converter for the JSONObject class while there is one for the String class.

    Second I had to pass a true value to the RestTemplate constructor. Failing to do so, I would get a 400 Bad Request.

    RestTemplate restTemplate = new RestTemplate(true);
    

    The true value tells the rest template to use the default converters. If someone knows why this is so, I'd be happy to know more about.

    Third I removed the unneeded Jackson converter:

    restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    

    With these things done, the request works just fine.

    Here is the full code:

    RestTemplate restTemplate = new RestTemplate(true);    
    User user = null;
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    try {
        JSONObject jsonCredentials = new JSONObject();
        jsonCredentials.put("email", REST_LOGIN);
        jsonCredentials.put("password", REST_PASSWORD);
        Log.e(Constants.APP_NAME, ">>>>>>>>>>>>>>>> JSON credentials " + jsonCredentials.toString());
        HttpEntity<String> entityCredentials = new HttpEntity<String>(jsonCredentials.toString(), httpHeaders);
        ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_HOST + ":" + REST_PORT + "/" + REST_APP + "/api/users/login",
                HttpMethod.POST, entityCredentials, User.class);
        if (responseEntity != null) {
            user = responseEntity.getBody();
        }
        return user;
    } catch (Exception e) {
        Log.e(Constants.APP_NAME, ">>>>>>>>>>>>>>>> " + e.getLocalizedMessage());
    }
    return null;
    

    I suspect there could be a way to explicitly use a Jackson converter and skip the true value in the rest template constructor, but this is just a guess.

    0 讨论(0)
  • 2020-12-18 22:00

    I know... is too late. But here I am.

    I fixed this sending a Java Object instead a JSONObject, something like this:

    JSONObject jsonCredentials = new JSONObject();
    jsonCredentials.put("email", REST_LOGIN);
    jsonCredentials.put("password", REST_PASSWORD);
    
    // Generic Object. It might be a DTO.
    Object jsonCredentialsObj = JsonUtils.stringToObject(jsonCredentials.toString(), Object.class);
    
    ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_HOST + ":" + REST_PORT + "/" + REST_APP + "/api/users/login",
            HttpMethod.POST, new HttpEntity<Object>(jsonCredentialsObj, httpHeaders), User.class);
    

    Where JsonUtils.stringToObject is my own convert utility.

    0 讨论(0)
  • 2020-12-18 22:06

    Quite late to reply, though I've just hitten the same problem and took me some time to solve it. So, I think I'd better share it and keep track of my solution.

    Actually, the exception thrown is totally misleading. Turned out the problem is not that the MappingJackson2HttpMessageConverter didn't know how to marshall my object -which sounded strange, being JSON-, but a configuration of the underlying ObjectMapper.

    What I did is to disable the property SerializationFeature.FAIL_ON_EMPTY_BEANS like that

    restTemplate = new RestTemplate();
    MappingJackson2HttpMessageConverter jsonHttpMessageConverter = new MappingJackson2HttpMessageConverter();
    jsonHttpMessageConverter.getObjectMapper().configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    restTemplate.getMessageConverters().add(jsonHttpMessageConverter);
    

    and everything started working as expected.

    0 讨论(0)
提交回复
热议问题