web-dev-qa-db-de.com

Anforderung konnte nicht geschrieben werden: Es wurde kein geeigneter HttpMessageConverter für Anforderungstyp [org.json.JSONObject] und Inhaltstyp [application/json] gefunden.

Ich versuche, eine POST - Anforderung mit einer JSON-Nutzlast an einen Remote-Server zu senden.

Dieser Befehl GET curl funktioniert einwandfrei:

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

Und dieser POST funktioniert auch gut:

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\" : \"[email protected]\", \"password\" : \"aaa\" }" -i

Und so versuche ich es in meiner Android-App nachzuahmen.

Die App funktioniert gut bei der ersten GET-Anfrage, gibt jedoch eine 400 Bad Request an der zweiten POST.

Hier ist der Code, der für die GET-Anfrage funktioniert:

  RestTemplate restTemplate = new RestTemplate();
  restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
  HttpHeaders httpHeaders = Common.createAuthenticationHeaders("[email protected]" + ":" + "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);

Hier ist der Quellcode für die Anforderung 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);

Aber es gibt die Nachricht:

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

Hier ist der Spring REST Controller:

@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;
    }
}

Der CredentialsResource-Klassencode:

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

}
9
Stephane

Recht spät, um zu antworten, obwohl ich gerade das gleiche Problem angesprochen habe und mir etwas Zeit gekostet habe, um es zu lösen. Ich denke, ich sollte es besser mitteilen und meine Lösung im Auge behalten.

Tatsächlich ist die geworfene Ausnahme völlig irreführend. Es stellte sich heraus, dass das Problem nicht darin bestand, dass der MappingJackson2HttpMessageConverter nicht wusste, wie ich mein Objekt zusammenstellen sollte, das sich als JSON befremdlich anhört, sondern eine Konfiguration der zugrunde liegenden ObjectMapper.

Was ich getan habe ist, deaktivieren die Eigenschaft SerializationFeature.FAIL_ON_EMPTY_BEANS so

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

und alles begann wie erwartet.

13
Stefano Cazzola

Ich musste ein paar Dinge tun, damit das funktioniert.

Zuerst musste ich das JSONObject in einen String konvertieren, wie in:

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

Der Grund dafür ist, dass für die JSONObject-Klasse kein Mapping-Nachrichtenkonverter vorhanden ist, während für die String-Klasse einer vorhanden ist.

Zweitens musste ich einen echten Wert an den RestTemplate-Konstruktor übergeben. Andernfalls würde ich eine 400 Bad Request erhalten.

RestTemplate restTemplate = new RestTemplate(true);

Der wahre Wert teilt der Restvorlage mit, die Standardkonverter zu verwenden. Wenn jemand weiß, warum das so ist, würde ich gerne mehr darüber erfahren.

Drittens entfernte ich den nicht benötigten Jackson-Konverter:

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

Wenn diese Dinge erledigt sind, funktioniert die Anfrage gut.

Hier ist der vollständige 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;

Ich vermute, es könnte eine Möglichkeit geben, einen Jackson-Konverter explizit zu verwenden und den wahren Wert im Restvorlagenkonstruktor zu überspringen. Dies ist jedoch nur eine Vermutung.

4
Stephane

Die Ausnahme "Kein passender HTTPMessageConverter gefunden" wird ausgelöst, wenn im Klassenpfad keine JSON-Implementierung vorhanden ist. Schauen Sie sich den RestTemplate-Quellcode an:

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

Fügen Sie Ihrem Klassenpfad eine JSON-Implementierung hinzu. Z.B.:

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

Ich weiß ... ist zu spät. Aber hier bin ich.

Ich habe dieses Problem behoben, indem ein Java-Objekt anstelle eines JSONObject gesendet wurde.

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

Wo JsonUtils.stringToObject ist mein eigenes Konvertierungsdienstprogramm.

0
Daniel Guerra

Spring's RestTemplate kann die Android org.json.JSONObject-Klasse nicht serialisieren. (Org.json.JSONObject ist in "normalem"/Desktop-Java nicht verfügbar.)

RestTemplate für Android unterstützt

Jackson JSON-Prozessor, Jackson 2.x und Google Gson.

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

Mit Jackson auf dem Klassenpfad sollte RestTemplate verstehen, wie man Java Beans serialisiert.

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

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

P.S. Es wäre ziemlich einfach, einen eigenen JSONObjectHttpMessageConverter zu schreiben, wenn Sie weiterhin JSONObject verwenden möchten. Ihr Konverter sollte nur .toString () aufrufen müssen.

0
teknopaul