web-dev-qa-db-fra.com

Quel est le moyen préféré de spécifier un en-tête de réponse "Emplacement" HTTP dans Spring MVC 3?

Quel est le moyen préféré de spécifier un en-tête de réponse "Emplacement" HTTP dans Spring MVC 3?

Pour autant que je sache, Spring fournira uniquement un "Lieu" en réponse à une redirection ("redirect: xyz" ou RedirectView). Toutefois, il existe des scénarios dans lesquels un Lieu doit être envoyé avec le corps de l'entité (par exemple, résultat d'un "201 créé").

Je crains que ma seule option soit de le spécifier manuellement:

httpServletResponse.setHeader("Location", "/x/y/z");

Est-ce correct? Y a-t-il une meilleure façon de s'attaquer à ce problème?

27
Brian Kent

À partir du printemps 3.1, le meilleur moyen de créer l'emplacement consiste à utiliser le paramètre UriComponentBuilder et à le définir sur la valeur ResponseEntity renvoyée. UriComponentBuilder est conscient du contexte et manipule avec des chemins relatifs:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createCustomer(UriComponentsBuilder b) {

    UriComponents uriComponents = 
        b.path("/customers/{id}").buildAndExpand(id);

    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(uriComponents.toUri());
    return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}

Depuis la version 4.1, vous pouvez le rendre encore plus court

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createCustomer(UriComponentsBuilder b) {

    UriComponents uriComponents = 
        b.path("/customers/{id}").buildAndExpand(id);

    return ResponseEntity.created(uriComponents.toUri()).build();
}

Merci à Dieter Hubau de l'avoir signalé.

70
Roman Konoval

L'exemple suivant est tiré du didacticiel Spring:

@RequestMapping(method = RequestMethod.POST)
ResponseEntity<?> add(@PathVariable String userId, @RequestBody Bookmark input) {
    this.validateUser(userId);

    return this.accountRepository
            .findByUsername(userId)
            .map(account -> {
                Bookmark result = bookmarkRepository.save(new Bookmark(account,
                        input.uri, input.description));

                URI location = ServletUriComponentsBuilder
                    .fromCurrentRequest().path("/{id}")
                    .buildAndExpand(result.getId()).toUri();

                return ResponseEntity.created(location).build();
            })
            .orElse(ResponseEntity.noContent().build());

}

Notez que ce qui suit calculera le chemin de contexte (URI) pour vous éviter la duplication de code et rendre votre application plus portable:

ServletUriComponentsBuilder
                    .fromCurrentRequest().path("/{id}")
12
Agustí Sánchez

Selon: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30

L'URI absolu doit être utilisé:

Location       = "Location" ":" absoluteURI  

Et leURIdevrait être échappé correctement:

http://www.ietf.org/rfc/rfc2396.txt

1
tszming

C'est une vieille question mais voici ce que vous pouvez faire si vous voulez laisser Spring construire réellement l'URI pour vous.

@RestController
@RequestMapping("/api/v1")
class JobsController {

  @PostMapping("/jobs")
  fun createJob(@RequestParam("use-gpu") useGPU: Boolean?): ResponseEntity<Unit> {

    val headers = HttpHeaders()

    val jobId = "TBD id"

    headers.location =
            MvcUriComponentsBuilder
                    .fromMethodName(JobsController::class.Java, "getJob", jobId)
                    .buildAndExpand(jobId)
                    .toUri()

    return ResponseEntity(headers, HttpStatus.CREATED)
  }

  @GetMapping("/job/{jobId}")
  fun getJob(@PathVariable jobId: String) = mapOf("id" to jobId)
}

Dans cet exemple (écrit en Kotlin mais similaire pour Java), l'URI de base est /api/v1 (défini en haut de la classe). Utiliser l'appel MvcUriComponentsBuilder.fromMethodName permet à Spring de déterminer l'URI complet correct. (MvcUriComponentsBuilder a été ajouté dans 4.0).

1
yan

Votre approche semble correcte, mais pour le garder propre, vous pouvez placer le code dans une variable HandlerInterceptor personnalisée qui ne se déclenche que lorsqu'il existe un HTTP 201, par exemple.

Voir ici pour plus d'informations.

0
earldouglas