Не удалось записать запрос: не найден подходящий HttpMessageConverter для типа запроса [org.json.JSONObject] и типа контента [application/json]

Я пытаюсь отправить запрос POST с полезной нагрузкой JSON на удаленный сервер.

Эта команда GET curl работает нормально:

curl -H "Accept:application/json" --user aaa@aaa.com:aaa "http://www.aaa.com:8080/aaa-project-rest/api/users/1" -i

И этот POST один тоже отлично работает:

curl -H "Accept:application/json" -H "Content-Type: application/json" "http://www.aaa.com:8080/aaa-project-rest/api/users/login" -X POST -d "{ \"email\" : \"aaa@aaa.com\", \"password\" : \"aaa\" }" -i

И поэтому я пытаюсь имитировать это в моем приложении для Android.

Приложение отлично работает при первом GET-запросе, но выдает 400 Bad Request во втором POST-запросе.

Вот код, который работает для запроса GET:

  RestTemplate restTemplate = new RestTemplate();
  restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
  HttpHeaders httpHeaders = Common.createAuthenticationHeaders("aaa@aaa.com" + ":" + "aaa");
  User user = null;
  ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_HOST + ":8080/aaa-project-rest/api/users/" + 1L, HttpMethod.GET, new HttpEntity<Object>(httpHeaders), User.class);

Вот исходный код для запроса POST:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
User user = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
JSONObject jsonCredentials = new JSONObject();
jsonCredentials.put("email", REST_LOGIN);
jsonCredentials.put("password", REST_PASSWORD);
ResponseEntity<User> responseEntity = restTemplate.exchange("http://" + REST_HOST + ":" + REST_PORT + "/" + REST_APP + "/api/users/login",
        HttpMethod.POST, new HttpEntity<Object>(jsonCredentials, httpHeaders), User.class);

Но это дает сообщение:

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

Вот контроллер Spring REST:

@RequestMapping(value = RESTConstants.SLASH + RESTConstants.LOGIN, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserResource> login(@Valid @RequestBody CredentialsResource credentialsResource, UriComponentsBuilder builder) {
    HttpHeaders responseHeaders = new HttpHeaders();
    User user = credentialsService.checkPassword(credentialsResource);
    userService.clearReadablePassword(user);
    if (user == null) {
        return new ResponseEntity<UserResource>(responseHeaders, HttpStatus.NOT_FOUND);
    } else {
        tokenAuthenticationService.addTokenToResponseHeader(responseHeaders, credentialsResource.getEmail());
        responseHeaders.setLocation(builder.path(RESTConstants.SLASH + RESTConstants.USERS + RESTConstants.SLASH + "{id}").buildAndExpand(user.getId()).toUri());
        UserResource createdUserResource = userResourceAssembler.toResource(user);
        ResponseEntity<UserResource> responseEntity = new ResponseEntity<UserResource>(createdUserResource, responseHeaders, HttpStatus.CREATED);
        return responseEntity;
    }
}

@RequestMapping(value = RESTConstants.SLASH + "{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserResource> findById(@PathVariable Long id, UriComponentsBuilder builder) {
    HttpHeaders responseHeaders = new HttpHeaders();
    User user = userService.findById(id);
    if (user == null) {
        return new ResponseEntity<UserResource>(responseHeaders, HttpStatus.NOT_FOUND);
    } else {
        UserResource userResource = userResourceAssembler.toResource(user);
        responseHeaders.setLocation(builder.path(RESTConstants.SLASH + RESTConstants.USERS + RESTConstants.SLASH + "{id}").buildAndExpand(user.getId()).toUri());
        ResponseEntity<UserResource> responseEntity = new ResponseEntity<UserResource>(userResource, responseHeaders, HttpStatus.OK);
        return responseEntity;
    }
}

Код класса CredentialsResource:

public class CredentialsResource extends ResourceSupport {

    @NotEmpty
    @Email
    private String email;
    @NotEmpty
    private String password;

    public CredentialsResource() {
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

4 ответа

Довольно поздно, чтобы ответить, хотя я только что столкнулся с той же проблемой и потратил некоторое время на ее решение. Так что, думаю, мне лучше поделиться этим и отследить свое решение.

На самом деле, выброшенное исключение полностью вводит в заблуждение. Оказалось, проблема не в том, что MappingJackson2HttpMessageConverter не знал, как маршалировать мой объект - который звучит странно, будучи JSON, - но конфигурация базового ObjectMapper,

Что я сделал, чтобы отключить свойство SerializationFeature.FAIL_ON_EMPTY_BEANS как это

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

и все начало работать как положено.

Я должен был сделать несколько вещей, чтобы заставить это работать.

Сначала я должен был преобразовать JSONObject в строку, как в:

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

Причина в том, что для класса JSONObject нет конвертера сообщений сопоставления, а для класса String - конвертера.

Во-вторых, мне нужно было передать истинное значение конструктору RestTemplate. В противном случае я получил бы 400 плохих запросов.

RestTemplate restTemplate = new RestTemplate(true);

Истинное значение указывает остальному шаблону использовать конвертеры по умолчанию. Если кто-то знает, почему это так, я был бы рад узнать больше о.

В-третьих, я удалил ненужный конвертер Джексона:

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

С этими вещами, запрос работает просто отлично.

Вот полный код:

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;

Я подозреваю, что может быть способ явно использовать конвертер Джексона и пропустить истинное значение в конструкторе остальных шаблонов, но это всего лишь предположение.

Выдается исключение "не найден подходящий HTTPMessageConverter", если в пути к классам присутствует реализация JSON. Взгляните на исходный код RestTemplate:

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());
    }
}

Добавьте реализацию JSON в ваш путь к классу. Е. г.:

implementation "com.fasterxml.jackson.core:jackson-databind:2.9.0"

Я знаю... слишком поздно. Но я здесь.

Я исправил это, посылая объект Java вместо JSONObject, что-то вроде этого:

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);

Где JsonUtils.stringToObject - моя собственная утилита конвертации.

Spring RestTemplate не знает, как сериализовать класс Android org.json.JSONObject. (org.json.JSONObject недоступен в "обычной"/ настольной Java)

RestTemplate для Android поддерживает

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

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

С Джексоном в пути к классам RestTemplate должен понимать, как сериализовать компоненты Java.

CredentialsResource cr = new CredentialsResource();
cr.setEmail(...);
cr.setPassword(...);

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

PS Было бы довольно легко написать свой собственный JSONObjectHttpMessageConverter, если бы вы хотели продолжить использование JSONObject, ваш конвертер должен просто вызвать.toString()

Другие вопросы по тегам