Подтвердить что ты не робот

Каков предпочтительный способ указания заголовка ответа "Местоположение" HTTP в Spring MVC 3?

Каков предпочтительный способ указания заголовка ответа "Местоположение" HTTP в Spring MVC 3?

Насколько я могу судить, Spring будет предоставлять только "Местоположение" в ответ на перенаправление ( "redirect: xyz" или RedirectView), однако есть сценарии, в которых местоположение должно быть отправлено вместе с телом объекта (например, в результате создания "201" ).

Я боюсь, что мой единственный параметр вручную укажет его:

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

Это правильно? Есть ли лучший способ решить эту проблему?

4b9b3361

Ответ 1

Как и в случае с spring 3.1, лучший способ создания местоположения - использовать параметр UriComponentBuilder и установить его в возвращаемый ResponseEntity. UriComponentBuilder знает контекст и манипулирует с относительными путями:

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

Начиная с версии 4.1 вы можете сделать ее еще короче

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

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

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

Спасибо Дитеру Хубау, чтобы указать на это.

Ответ 2

Следующий пример из 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());

}

Обратите внимание, что следующее вычислит путь контекста (URI), чтобы избежать дублирования кода и сделать ваше приложение более переносимым:

ServletUriComponentsBuilder
                    .fromCurrentRequest().path("/{id}")

Ответ 3

Это старый вопрос, но вот что вы можете сделать, если хотите, чтобы Spring действительно создала для вас URI.

@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) = ... // fetch job
}

В этом примере (который написан на Kotlin, но похож на Java), базовый URI - /api/v1 (определенный в верхней части класса). Использование вызова MvcUriComponentsBuilder.fromMethodName позволяет Spring определить правильный полный URI. (MvcUriComponentsBuilder был добавлен в 4.0).

Ответ 5

Ваш подход кажется прекрасным, но чтобы он был чистым, вы можете поместить код в пользовательский HandlerInterceptor, который запускается только тогда, когда есть HTTP 201.

Подробнее см. здесь.