如何解决如何通过REST控制器用Spring引导重写URL?
假设我的父类具有以下控制器:
@RestController
public class BusinessController extends RootController {
@GetMapping(value = "users",produces = {"application/json"})
@ResponseBody
public String users() {
return "{ \"users\": [] }"
}
@GetMapping(value = "companies",produces = {"application/json"})
@ResponseBody
public String companies() {
return "{ \"companies\": [] }"
}
}
@RestController
@RequestMapping(path = "api")
public class RootController {
}
通过调用此类URL检索数据:
http://app.company.com/api/users
http://app.company.com/api/companies
现在,我想将/api
路径重命名为/rest
,但是通过返回301
HTTP状态代码以及新URI来使其保持“可用”状态
例如客户要求:
GET /api/users HTTP/1.1
Host: app.company.com
服务器请求:
HTTP/1.1 301 Moved Permanently
Location: http://app.company.com/rest/users
因此,我计划在父控制器中从"api"
更改为"rest"
:
@RestController
@RequestMapping(path = "rest")
public class RootController {
}
然后引入一个“旧版”控制器:
@RestController
@RequestMapping(path = "api")
public class LegacyRootController {
}
但是现在如何使其“重写”“旧版” URI?
这就是我一直在努力的问题,无论在StackOverflow还是其他地方,我都找不到与Spring相关的任何东西。
我也有许多控制器和许多方法端点,所以我不能手动执行此操作(即通过编辑每个@ RequestMapping / @ GetMapping注释)。
我正在处理的项目基于Spring Boot 2.1
编辑:我删除了/business
路径,因为实际上继承在默认情况下不起作用(请参见Spring MVC @RequestMapping Inheritance或Modifying @RequestMappings on startup之类的问题和答案)-对此感到抱歉。>
解决方法
由于看起来您想保留301,但也希望它返回响应,因此您可以选择将RootController
连接到LegacyRootController
这样,您就可以提供RootController
中具有的逻辑复用,但返回不同的响应代码并在LegacyRootController
上提供不同的路径
@RestController
@RequestMapping(path = "api")
public class LegacyRootController {
private final RootController rootController;
public LegacyRootController(RootController rootController) {
this.rootController = rootController;
}
@GetMapping(value = "users",produces = {"application/json"})
@ResponseStatus(HttpStatus.MOVED_PERMANENTLY) // Respond 301
@ResponseBody
public String users() {
return rootController.users(); // Use rootController to provide appropriate response.
}
@GetMapping(value = "companies",produces = {"application/json"})
@ResponseStatus(HttpStatus.MOVED_PERMANENTLY)
@ResponseBody
public String companies() {
return rootController.companies();
}
}
这将使您可以为/api/users
提供301响应,同时还可以为/rest/users
提供标准响应。
如果您想添加Location标头,则可以让LegacyRootController
返回ResponseEntity
来提供正文,代码和标头值。
@GetMapping(value = "users",produces = {"application/json"})
public ResponseEntity<String> users() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setLocation("...");
return new ResponseEntity<String>(rootController.users(),responseHeaders,HttpStatus.MOVED_PERMANENTLY);
}
如果要服务于不提供不同状态代码的多个端点,则只需提供多个路径即可。
@RequestMapping(path = {"api","rest"})
我终于找到了一种以javax.servlet.Filter
和org.springframework.web.server.WebFilter
实现的方式来实现。
实际上,我引入了Adapter模式以同时转换两者:
-
org.springframework.http.server.ServletServerHttpResponse
(非反应性)和 -
org.springframework.http.server.reactive.ServerHttpResponse
(活动)
因为与Spring的HTTP请求的包装器共享org.springframework.http.HttpRequest
(让我同时访问URI
和HttpHeaders
)相反,响应的包装器没有共享一个公用接口它,所以我不得不模仿一个(此处故意以类似的方式命名,HttpResponse
)。
@Component
public class RestRedirectWebFilter implements Filter,WebFilter {
@Override
public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain chain)
throws IOException,ServletException {
ServletServerHttpRequest request = new ServletServerHttpRequest((HttpServletRequest) servletRequest);
ServletServerHttpResponse response = new ServletServerHttpResponse((HttpServletResponse) servletResponse);
if (actualFilter(request,adapt(response))) {
chain.doFilter(servletRequest,servletResponse);
}
}
@Override
public Mono<Void> filter(ServerWebExchange exchange,WebFilterChain chain) {
if (actualFilter(exchange.getRequest(),adapt(exchange.getResponse()))) {
return chain.filter(exchange);
} else {
return Mono.empty();
}
}
/**
* Actual filtering.
*
* @param request
* @param response
* @return boolean flag specifying if filter chaining should continue.
*/
private boolean actualFilter(HttpRequest request,HttpResponse response) {
URI uri = request.getURI();
String path = uri.getPath();
if (path.startsWith("/api/")) {
String newPath = path.replaceFirst("/api/","/rest/");
URI location = UriComponentsBuilder.fromUri(uri).replacePath(newPath).build().toUri();
response.getHeaders().setLocation(location);
response.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
response.flush();
return false;
}
return true;
}
interface HttpResponse extends HttpMessage {
void setStatusCode(HttpStatus status);
void flush();
}
private HttpResponse adapt(ServletServerHttpResponse response) {
return new HttpResponse() {
public HttpHeaders getHeaders() {
return response.getHeaders();
}
public void setStatusCode(HttpStatus status) {
response.setStatusCode(status);
}
public void flush() {
response.close();
}
};
}
private HttpResponse adapt(org.springframework.http.server.reactive.ServerHttpResponse response) {
return new HttpResponse() {
public HttpHeaders getHeaders() {
return response.getHeaders();
}
public void setStatusCode(HttpStatus status) {
response.setStatusCode(status);
}
public void flush() {
response.setComplete();
}
};
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。