Solicitação POST via RestTemplate em JSON

126

Não encontrei nenhum exemplo de como resolver meu problema, por isso quero pedir ajuda. Não posso simplesmente enviar uma solicitação POST usando o objeto RestTemplate no JSON

Toda vez que eu recebo:

org.springframework.web.client.HttpClientErrorException: 415 Tipo de mídia não suportado

Eu uso RestTemplate desta maneira:

...
restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> list = new ArrayList<HttpMessageConverter<?>>();
list.add(new MappingJacksonHttpMessageConverter());
restTemplate.setMessageConverters(list);
...
Payment payment= new Payment("Aa4bhs");
Payment res = restTemplate.postForObject("http://localhost:8080/aurest/rest/payment", payment, Payment.class);

Qual é a minha culpa?

Johnny B
fonte
1
@troyfolger a url não é mais válido
Noremac
Graças - este link está trabalhando como esta escrito: spring.io/guides/gs/consuming-rest
troyfolger
Para resolver o problema específico do OP, acima, você provavelmente está perdendo um cabeçalho HTTP com o tipo de conteúdo apropriado, consulte a resposta de morganw09dev abaixo.
troyfolger
Esses problemas estão principalmente relacionados à configuração da API do servidor. Você testa a API do servidor usando um cliente autônomo (como o Postman) e replica os mesmos cabeçalhos em sua solicitação. Pelo menos no meu caso, isso fez o truque.
Linus
1
@Johnny B, se isso tiver sido respondido, marque a resposta
Vishal

Respostas:

161

Essa técnica funcionou para mim:

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

HttpEntity<String> entity = new HttpEntity<String>(requestJson, headers);
ResponseEntity<String> response = restTemplate.put(url, entity);

Eu espero que isso ajude

Kanu Dialani
fonte
3
explique qual linha deve retornar o resultado da solicitação http
gstackoverflow 13/15
Para mim, não foi necessário especificar nenhum cabeçalho. Eu usei o HttpEntity que usa um único parâmetro.
Constantino Cronemberger
24
método .put()é void!
membersound
4
Usando postForEntity(url, entity, String.class)obras no lugar deput(url, entity)
Janac Meena
1
@ Kanu, requestJson é válido JSON String ou algo mais?
Deva
95

Encontrei esse problema ao tentar depurar um terminal REST. Aqui está um exemplo básico usando a classe RestTemplate do Spring para fazer uma solicitação POST que eu usei. Demorei bastante tempo para reunir o código de diferentes lugares para obter uma versão funcional.

RestTemplate restTemplate = new RestTemplate();

String url = "endpoint url";
String requestJson = "{\"queriedQuestion\":\"Is there pain in your hand?\"}";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String answer = restTemplate.postForObject(url, entity, String.class);
System.out.println(answer);

O analisador JSON em particular que meu ponto de extremidade restante estava usando aspas duplas necessárias em torno dos nomes de campos; é por isso que escapei das aspas duplas na minha requestJson String.

Morgan Kenyon
fonte
u pode por favor me ajudar neste stackoverflow.com/questions/42240927/…
FaisalAhmed
O Spring pode usar os conversores de mensagens para converter automaticamente o objeto Java em json, como na API Restful com RestTemplate?
cair
1
Definir o tipo de mídia como APPLICATION_JSON é a chave para resolver o problema.
Pete T
Resolvi meu problema usando HttpEntity <> string entity = new HttpEntity <>> (requestJson, headers); esta linha
Ved Prakash
76

Eu tenho usado o resto modelo com JSONObjects da seguinte maneira:

// create request body
JSONObject request = new JSONObject();
request.put("username", name);
request.put("password", password);

// set headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);

// send request and parse result
ResponseEntity<String> loginResponse = restTemplate
  .exchange(urlString, HttpMethod.POST, entity, String.class);
if (loginResponse.getStatusCode() == HttpStatus.OK) {
  JSONObject userJson = new JSONObject(loginResponse.getBody());
} else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) {
  // nono... bad credentials
}
Mikael Lepistö
fonte
Obrigado - o método JSONObject toString foi útil para mim, pois me ajudou a obter meu JSONString preciso.
Simon
1
Como desenvolver o código acima para isso: curl -vvv -X POST " localhost: 8080 / SillyService_SRC / oauth /… "?
Pra_A
@Mikael Lepistö Como posso recuperar esses parâmetros do json no final do servidor?
KJEjava48
@ KJEjava48 Eu não entendo o que você quer dizer ... este é o código do lado do servidor na resposta. Se você está pensando em analisar a resposta do json, isso depende da estrutura que você está usando.
Mikael Lepistö
@ MikaelLepistö Quero dizer como analisar a resposta do json na outra extremidade, incluindo como receber a resposta em java? Você postou apenas o código para uma extremidade (ou seja, do lado do servidor).
KJEjava48
13

Conforme especificado aqui , acho que você precisa adicionar um messageConverterpara MappingJacksonHttpMessageConverter

Raghuram
fonte
9

Se você estiver usando o Spring 3.0, uma maneira fácil de evitar a exceção org.springframework.web.client.HttpClientErrorException: 415 Tipo de mídia não suportado , é incluir os arquivos jar jackson no caminho de classe e usar o mvc:annotation-drivenelemento config. Conforme especificado aqui .

Eu estava puxando meu cabelo tentando descobrir por que o aplicativo mvc-ajax funcionava sem nenhuma configuração especial para o MappingJacksonHttpMessageConverter. Se você leu o artigo que eu vinculei acima de perto:

Debaixo das capas, o Spring MVC delega para um HttpMessageConverter para executar a serialização. Nesse caso, o Spring MVC chama um MappingJacksonHttpMessageConverter construído no processador Jackson JSON. Essa implementação é ativada automaticamente quando você usa o elemento de configuração controlado por anotação mvc: com Jackson presente em seu caminho de classe .

Mike G
fonte
7

O erro "415 Tipo de mídia não suportado" está informando que o servidor não aceitará sua solicitação POST. Sua solicitação está absolutamente correta, é o servidor que está configurado incorretamente.

MappingJacksonHttpMessageConverterconfigurará automaticamente o cabeçalho do tipo de conteúdo da solicitação como application/json, e meu palpite é que seu servidor está rejeitando isso. Você não nos contou nada sobre a configuração do servidor, por isso não posso aconselhá-lo sobre isso.

skaffman
fonte
7

Estou fazendo assim e funciona.

HttpHeaders headers = createHttpHeaders(map);
public HttpHeaders createHttpHeaders(Map<String, String> map)
{   
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    for (Entry<String, String> entry : map.entrySet()) {
        headers.add(entry.getKey(),entry.getValue());
    }
    return headers;
}

// Passa os cabeçalhos aqui

 String requestJson = "{ // Construct your JSON here }";
logger.info("Request JSON ="+requestJson);
HttpEntity<String> entity = new HttpEntity<String>(requestJson, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
logger.info("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
logger.info("Response ="+response.getBody());

Espero que isto ajude

Yakhoob
fonte
4

Eu estava tendo esse problema e estou usando o RestTemplate do Spring no cliente e o Spring Web no servidor. Ambas as APIs têm um relatório de erros muito ruim, tornando-as extremamente difíceis de desenvolver.

Depois de muitas horas tentando todos os tipos de experimentos, descobri que o problema estava sendo causado ao passar uma referência nula para o corpo do POST, em vez da lista esperada. Presumo que RestTemplate não possa determinar o tipo de conteúdo de um objeto nulo, mas não reclame. Depois de adicionar os cabeçalhos corretos, comecei a receber uma exceção diferente do lado do servidor no Spring antes de inserir meu método de serviço.

A correção era passar uma lista vazia do cliente em vez de nulo. Nenhum cabeçalho é necessário, pois o tipo de conteúdo padrão é usado para objetos não nulos.

Alex Worden
fonte
3

Este código está funcionando para mim;

RestTemplate restTemplate = new RestTemplate();
Payment payment = new Payment("Aa4bhs");
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("payment", payment);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(map, headerObject);

Payment res = restTemplate.postForObject(url, httpEntity, Payment.class);
Ganesh
fonte
Eu estou usando uma abordagem muito semelhante e não funcionou para mim. por alguma razão, meu equivalente do seu 'mapa' não está sendo convertido para json ou incluído como corpo de saída, ou seja, o serviço de destino NÃO vê nenhuma carga útil.
abdel 24/07
2

Se você não quiser processar a resposta

private RestTemplate restTemplate = new RestTemplate();
restTemplate.postForObject(serviceURL, request, Void.class);

Se você precisar de resposta ao processo

String result = restTemplate.postForObject(url, entity, String.class);
Vipindas Gopalan
fonte
0

Para mim, ocorreu um erro nesta configuração:

AndroidAnnotations Spring Android RestTemplate Module e ...

GsonHttpMessageConverter

As anotações do Android têm alguns problemas com isso convertidos para gerar POSTsolicitação sem parâmetro. Simplesmente parâmetro new Object()resolveu isso para mim.

Mateusz Jablonski
fonte
0

Por que trabalhar mais do que você precisa? postForEntityaceita um Mapobjeto simples como entrada. O seguinte funciona bem para mim enquanto escrevia testes para um determinado ponto de extremidade REST no Spring. Eu acredito que é a maneira mais simples possível de fazer uma solicitação JSON POST na primavera:

@Test
public void shouldLoginSuccessfully() {
  // 'restTemplate' below has been @Autowired prior to this
  Map map = new HashMap<String, String>();
  map.put("username", "bob123");
  map.put("password", "myP@ssw0rd");
  ResponseEntity<Void> resp = restTemplate.postForEntity(
      "http://localhost:8000/login",
      map,
      Void.class);
  assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
}
eriegz
fonte
0

Se você não deseja mapear o JSON sozinho, faça o seguinte:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
ResponseEntity<String> result = restTemplate.postForEntity(uri, yourObject, String.class);
moritz.vieli
fonte
0

Eu tentei o seguinte na inicialização da primavera:

ParameterizedTypeReference<Map<String, Object>> typeRef = new ParameterizedTypeReference<Map<String, Object>>() {};
public Map<String, Object> processResponse(String urlendpoint)
{
    try{
    
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        //reqobj
        JSONObject request = new JSONObject();
        request.put("username", name);
        //Or Hashmap 
        Map<String, Object> reqbody =  new HashMap<>();
        reqbody.put("username",username);
        Gson gson = new Gson();//mvn plugin to convert map to String
        HttpEntity<String> entity = new HttpEntity<>( gson.toJson(reqbody), headers);
        ResponseEntity<Map<String, Object>> response = resttemplate.exchange(urlendpoint, HttpMethod.POST, entity, typeRef);//example of post req with json as request payload
        if(Integer.parseInt(response.getStatusCode().toString()) == HttpURLConnection.HTTP_OK)
        {
            Map<String, Object>  responsedetails = response.getBody();
            System.out.println(responsedetails);//whole json response as map object
            return responsedetails;
        }
    } catch (Exception e) {
        // TODO: handle exception
        System.err.println(e);
    }
    return null;
}
Parameshwar
fonte