Java 在 Spring MVC 3 中指定 HTTP“位置”响应头的首选方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3318912/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 22:05:05  来源:igfitidea点击:

What is the preferred way to specify an HTTP "Location" Response Header in Spring MVC 3?

javarestspring-mvc

提问by Brian Kent

What is the preferred way to specify an HTTP "Location" Response Header in Spring MVC 3?

在 Spring MVC 3 中指定 HTTP“位置”响应头的首选方法是什么?

As far as I can tell, Spring will only provide a "Location" in response to a redirect ("redirect:xyz" or RedirectView), however there are scenarios where a Location should be sent along with the entity body (ex, as a result of a "201 Created").

据我所知,Spring 只会提供一个“位置”来响应重定向(“redirect:xyz”或 RedirectView),但是在某些情况下,位置应该与实体主体一起发送(例如,作为“201 Created”的结果)。

I'm afraid my only option is manually specifying it:

恐怕我唯一的选择是手动指定它:

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

Is this correct? Is there a better way to tackle this problem?

这样对吗?有没有更好的方法来解决这个问题?

采纳答案by Roman Konoval

The key point is to use UriComponentsBuilder. There are several ways how you can get the instance of it

关键是使用UriComponentsBuilder. 有几种方法可以获取它的实例

  1. preconfigured UriComponentsBuilderfrom MvcUriComponentsBuilder
  2. UriComponentsBuilderinjected as parameter to method
  1. 预配置UriComponentsBuilderMvcUriComponentsBuilder
  2. UriComponentsBuilder作为参数注入方法

Preconfigured UriComponentsBuilderfrom MvcUriComponentsBuilder

预配置UriComponentsBuilderMvcUriComponentsBuilder

This way you can get UriComponentsBuilderthat is configured to produce URIthat points to some controller methods with predefined parameters.

通过这种方式,您可以将UriComponentsBuilder其配置为生成URI指向具有预定义参数的某些控制器方法。

Here is example from the javadocfor MvcUriComponentsBuilder:

下面是例如,从的javadocMvcUriComponentsBuilder

For example, given this controller:

例如,给定这个控制器:

 @RequestMapping("/people/{id}/addresses")
 class AddressController {

   @RequestMapping("/{country}")
   public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) { ... }

   @RequestMapping(value="/", method=RequestMethod.POST)
   public void addAddress(Address address) { ... }
 }
 A UriComponentsBuilder can be created:
 // Inline style with static import of "MvcUriComponentsBuilder.on"

 MvcUriComponentsBuilder.fromMethodCall(
    on(AddressController.class).getAddressesForCountry("US")).buildAndExpand(1);

Another options which sometimes may be preferable is to specify controller method by name:

有时可能更可取的另一个选项是按名称指定控制器方法:

UriComponents uriComponents = MvcUriComponentsBuilder.fromMethodName(
    AddressController.class, "getAddressesForCountry", "US").buildAndExpand(1);
URI nextUri = uriComponents.toUri();

UriComponentsBuilderinjected as parameter to method

UriComponentsBuilder作为参数注入方法

As of spring 3.1 Locationcan be crafted using UriComponentBuilderparameter and set it to the returned ResponseEntity. UriComponentBuilderis aware of the context and manipulates with relative paths:

从 spring 3.1 开始,Location可以使用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);
}

Since version 4.1 you can make it even shorter

从 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();
}

Thanks to Dieter Hubau to pointing this out.

感谢 Dieter Hubau 指出这一点。

回答by tszming

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

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

Absolute URI should be used:

应该使用绝对 URI:

Location       = "Location" ":" absoluteURI  

And the URIshould be escaped properly:

URI应该正确转义:

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

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

回答by earldouglas

Your approach seems fine, but to keep it clean you could put the code inside a custom HandlerInterceptorthat only triggers when there's an HTTP 201, for example.

您的方法看起来不错,但为了保持干净,您可以将代码放在一个自定义HandlerInterceptor中,例如,只有在有 HTTP 201 时才会触发。

See herefor more information.

请参阅此处了解更多信息。

回答by yan

It's an old question but here is what you can do if you want to let Spring really build the URI for you.

这是一个老问题,但如果您想让 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
}

In this example (which is written in Kotlin but similar for java), the base URI is /api/v1(defined at the top of the class). Using MvcUriComponentsBuilder.fromMethodNamecall lets Spring figure out the proper full URI. (MvcUriComponentsBuilderwas added in 4.0).

在这个例子中(它是用 Kotlin 编写的,但类似于 java),基本 URI 是/api/v1(在类的顶部定义)。使用MvcUriComponentsBuilder.fromMethodNamecall 让 Spring 找出正确的完整 URI。(MvcUriComponentsBuilder在 4.0 中添加)。

回答by Agustí Sánchez

The following example is from spring tutorial:

以下示例来自 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());

}

Take note that the following will compute the context path (URI) for you avoiding code duplication and making your application more portable:

请注意,以下内容将为您计算上下文路径 (URI),以避免代码重复并使您的应用程序更具可移植性:

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