java - studio - Realización de solicitudes POST autenticadas con Spring RestTemplate para Android
spring for java (4)
Tengo una API RESTful con la que intento conectarme a través de Android y RestTemplate. Todas las solicitudes a la API se autentican con Autenticación HTTP, mediante la configuración de los encabezados de HttpEntity y luego mediante el método de exchange()
RestTemplate.
Todas las solicitudes GET funcionan muy bien de esta manera, pero no puedo encontrar la manera de realizar solicitudes POST autenticadas. postForObject
y postForEntity
manejan los POST, pero no tienen una manera fácil de configurar los encabezados de Autenticación.
Entonces para GETs, esto funciona genial:
HttpAuthentication httpAuthentication = new HttpBasicAuthentication("username", "password");
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(httpAuthentication);
HttpEntity<?> httpEntity = new HttpEntity<Object>(requestHeaders);
MyModel[] models = restTemplate.exchange("/api/url", HttpMethod.GET, httpEntity, MyModel[].class);
Pero los POST aparentemente no funcionan con exchange()
ya que nunca envía los encabezados personalizados y no veo cómo establecer el cuerpo de la solicitud usando exchange()
.
¿Cuál es la forma más fácil de realizar solicitudes POST autenticadas desde RestTemplate?
Hace poco estuve lidiando con un problema cuando intentaba pasar la autenticación al hacer una llamada REST desde Java, y aunque las respuestas en este hilo (y otros hilos) me ayudaron, aún había un poco de prueba y error involucrado para conseguirlo trabajando.
Lo que funcionó para mí fue codificar las credenciales en Base64
y agregarlas como encabezados de Autorización básica. Luego los agregué como HttpEntity
a restTemplate.postForEntity
, que me dio la respuesta que necesitaba.
Aquí está la clase que escribí para esto en su totalidad (extendiendo RestTemplate):
public class AuthorizedRestTemplate extends RestTemplate{
private String username;
private String password;
public AuthorizedRestTemplate(String username, String password){
this.username = username;
this.password = password;
}
public String getForObject(String url, Object... urlVariables){
return authorizedRestCall(this, url, urlVariables);
}
private String authorizedRestCall(RestTemplate restTemplate,
String url, Object... urlVariables){
HttpEntity<String> request = getRequest();
ResponseEntity<String> entity = restTemplate.postForEntity(url,
request, String.class, urlVariables);
return entity.getBody();
}
private HttpEntity<String> getRequest(){
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + getBase64Credentials());
return new HttpEntity<String>(headers);
}
private String getBase64Credentials(){
String plainCreds = username + ":" + password;
byte[] plainCredsBytes = plainCreds.getBytes();
byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
return new String(base64CredsBytes);
}
}
Muy útil tuve un escenario ligeramente diferente donde el xml de la solicitud era en sí mismo el cuerpo del POST y no un param. Para eso, se puede usar el siguiente código: publicar como una respuesta en caso de que alguien más tenga problemas similares.
final HttpHeaders headers = new HttpHeaders();
headers.add("header1", "9998");
headers.add("username", "xxxxx");
headers.add("password", "xxxxx");
headers.add("header2", "yyyyyy");
headers.add("header3", "zzzzz");
headers.setContentType(MediaType.APPLICATION_XML);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
final HttpEntity<MyXmlbeansRequestDocument> httpEntity = new HttpEntity<MyXmlbeansRequestDocument>(
MyXmlbeansRequestDocument.Factory.parse(request), headers);
final ResponseEntity<MyXmlbeansResponseDocument> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity,MyXmlbeansResponseDocument.class);
log.info(responseEntity.getBody());
Ok encontré la respuesta. exchange()
es la mejor manera. Curiosamente, la clase HttpEntity
no tiene un método setBody()
(tiene getBody()
), pero aún es posible establecer el cuerpo de la solicitud, a través del constructor.
// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("field", "value");
// Note the body object as first parameter!
HttpEntity<?> httpEntity = new HttpEntity<Object>(body, requestHeaders);
MyModel model = restTemplate.exchange("/api/url", HttpMethod.POST, httpEntity, MyModel.class);
Un enfoque ligeramente diferente:
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("HeaderName", "value");
headers.add("Content-Type", "application/json");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<ObjectToPass> request = new HttpEntity<ObjectToPass>(objectToPass, headers);
restTemplate.postForObject(url, request, ClassWhateverYourControllerReturns.class);