web-dev-qa-db-fra.com

Envoi d'une requête GET avec des en-têtes d'authentification à l'aide de restTemplate

J'ai besoin de récupérer des ressources de mon serveur en envoyant une demande GET avec certains en-têtes d'autorisation à l'aide de RestTemplate.

Après avoir examiné docs , j'ai remarqué qu'aucune des méthodes GET n'accepte les en-têtes en tant que paramètre et que le seul moyen d'envoyer des en-têtes tels qu'accept et Authorization est d'utiliser exchange méthode.

Comme il s’agit d’une action très élémentaire, je me demande si quelque chose me manque et qu’il existe un autre moyen plus facile de le faire.

67
special0ne

Vous ne manquez rien. RestTemplate#exchange(..) est la méthode appropriée à utiliser pour définir les en-têtes de requête.

Voici un exemple (avec POST, mais remplacez-le simplement par GET et utilisez l'entité souhaitée).

Voici un autre exemple.

Notez qu'avec un élément GET, votre entité de requête ne doit contenir aucun élément (à moins que votre API ne le prévoie, mais cela irait à l'encontre de la spécification HTTP). Ce peut être une chaîne vide.

59

Vous pouvez utiliser postForObject avec un HttpEntity. Cela ressemblerait à ceci:

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer "+accessToken);

HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
String result = restTemplate.postForObject(url, entity, String.class);

Dans une demande GET, vous n'envoyez généralement pas de corps (c'est autorisé, mais cela ne sert à rien). Pour ajouter des en-têtes sans câbler différemment RestTemplate, utilisez directement les méthodes exchange ou execute. Les raccourcis de get ne supportent pas la modification d'en-tête.

L'asymétrie est un peu bizarre à première vue, peut-être que cela va être corrigé dans les futures versions de Spring.

40
iwein

Voici un exemple très simple avec l'authentification de base, les en-têtes et la gestion des exceptions ...

private HttpHeaders createHttpHeaders(String user, String password)
{
    String notEncoded = user + ":" + password;
    String encodedAuth = "Basic " + Base64.getEncoder().encodeToString(notEncoded.getBytes());
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.add("Authorization", encodedAuth);
    return headers;
}

private void doYourThing() 
{
    String theUrl = "http://blah.blah.com:8080/rest/api/blah";
    RestTemplate restTemplate = new RestTemplate();
    try {
        HttpHeaders headers = createHttpHeaders("fred","1234");
        HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
        ResponseEntity<String> response = restTemplate.exchange(theUrl, HttpMethod.GET, entity, String.class);
        System.out.println("Result - status ("+ response.getStatusCode() + ") has body: " + response.hasBody());
    }
    catch (Exception eek) {
        System.out.println("** Exception: "+ eek.getMessage());
    }
}
21
Dave

Toutes ces réponses semblent être incomplètes et/ou compliquées. En regardant l’interface RestTemplate, on dirait bien qu’elle est destinée à recevoir un ClientHttpRequestFactory, puis que requestFactory sera utilisé pour créer la demande, y compris toute personnalisation des en-têtes, du corps et des paramètres de la demande.

Soit vous avez besoin d’un ClientHttpRequestFactory universel à injecter dans un seul RestTemplate partagé ou vous devez obtenir une nouvelle instance de modèle via new RestTemplate(myHttpRequestFactory).

Malheureusement, la création d'une telle usine ne semble pas très simple, même si vous souhaitez simplement définir un en-tête d'autorisation unique, ce qui est assez frustrant si l'on considère une exigence commune probable, mais au moins cela permet une utilisation facile, par exemple , votre en-tête Authorization peut être créé à partir de données contenues dans un objet Spring-Security Authorization. Vous pouvez ensuite créer une fabrique qui définit AuthorizationHeader sortant pour chaque demande en effectuant SecurityContextHolder.getContext().getAuthorization(), puis en complétant l'en-tête, avec des vérifications nulles, le cas échéant. . Désormais, tous les appels restants sortants effectués avec ce RestTemplate auront l'en-tête d'autorisation approprié.

Sans mettre davantage l'accent sur le mécanisme HttpClientFactory, qui fournit des classes de base simples à surcharger pour les cas courants tels que l'ajout d'un en-tête unique aux demandes, la plupart des méthodes pratiques de Nice, RestTemplate, finissent par être une perte de temps, car elles ne peuvent que rarement être utilisées. utilisé.

Je voudrais voir quelque chose de simple comme celui-ci mis à disposition

@Configuration
public class MyConfig {
  @Bean
  public RestTemplate getRestTemplate() {
    return new RestTemplate(new AbstractHeaderRewritingHttpClientFactory() {
        @Override
        public HttpHeaders modifyHeaders(HttpHeaders headers) {
          headers.addHeader("Authorization", computeAuthString());
          return headers;
        }
        public String computeAuthString() {
          // do something better than this, but you get the idea
          return SecurityContextHolder.getContext().getAuthorization().getCredential();
        }
    });
  }
}

Pour le moment, il est plus difficile d'interagir avec l'interface des ClientHttpRequestFactory disponibles. Encore mieux serait un wrapper abstrait pour les implémentations d’usine existantes qui leur donne l’apparence d’un objet plus simple tel que AbstractHeaderRewritingRequestFactory dans le but de ne remplacer que cet élément de fonctionnalité. À l'heure actuelle, ils ont un but très général, de sorte que même écrire ces enveloppes est un travail de recherche complexe.

8
ideasculptor