如何通过REST控制器用Spring(引导)重写URL? [英] How to rewrite URLs with Spring (Boot) via REST Controllers?

查看:89
本文介绍了如何通过REST控制器用Spring(引导)重写URL?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我的父类具有以下控制器:

Let's say I have the following controller with its parent class:

@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来检索数据:

Data is retrieved by calling such URL's:

http://app.company.com/api/users
http://app.company.com/api/companies

现在让我们说我想将/api路径重命名为/rest,但是将其保持为"available".通过返回301 HTTP状态代码以及新的URI

Now let's say I want to rename the /api path to /rest but keep it "available" by returning a 301 HTTP status code alongside the new URI's

例如客户要求:

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":

So I plan to change from "api" to "rest" in my parent controller:

@RestController
@RequestMapping(path = "rest")
public class RootController {

}

然后引入旧版"控制器:

then introduce a "legacy" controller:

@RestController
@RequestMapping(path = "api")
public class LegacyRootController {

}

但是现在如何使它重写"? 传统" URI是?

but now how to make it "rewrite" the "legacy" URI's?

这就是我正在努力解决的问题,无论在StackOverflow还是其他地方,我都找不到与Spring相关的任何东西.

That's what I'm struggling with, I can't find anything Spring-related on the matter, whether on StackOverflow or elsewhere.

我也有许多控制器和许多方法端点,所以我无法手动执行此操作(即,通过编辑每个@ RequestMapping/@ GetMapping注释).

Also I have many controllers AND many methods-endpoints so I can not do this manually (i.e. by editing every @RequestMapping/@GetMapping annotations).

我正在处理的项目基于Spring Boot 2.1

And project I'm working on is based on Spring Boot 2.1

我删除了/business路径,因为实际上继承在默认情况下"不起作用. (请参见 Spring MVC @RequestMapping继承

I removed the /business path because actually inheritance doesn't work "by default" (see questions & answers like Spring MVC @RequestMapping Inheritance or Modifying @RequestMappings on startup ) - sorry for that.

推荐答案

我终于找到了一种实现此方法的方法,既可以作为javax.servlet.Filter又可以作为org.springframework.web.server.WebFilter实现.

I finally found a way to implement this, both as a javax.servlet.Filter AND a org.springframework.web.server.WebFilter implementation.

事实上,我引入了Adapter模式以同时转换两者:

In fact, I introduced the Adapter pattern in order to transform both:

  • org.springframework.http.server.ServletServerHttpResponse(非反应性)和
  • org.springframework.http.server.reactive.ServerHttpResponse(反应式)
  • org.springframework.http.server.ServletServerHttpResponse (non-reactive) and
  • org.springframework.http.server.reactive.ServerHttpResponse (reactive)

因为与Spring的HTTP请求的包装器共享org.springframework.http.HttpRequest(让我同时访问URIHttpHeaders)相反,响应的包装器没有共享这样做的公共接口,所以我不得不模拟一个(此处特意以类似的方式命名,称为HttpResponse).

because on the contrary of the Spring's HTTP requests' wrappers which share org.springframework.http.HttpRequest (letting me access both URI and HttpHeaders), the responses's wrappers do not share a common interface that does it, so I had to emulate one (here purposely named in a similar fashion, 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();
            }
        };
    }

}

这篇关于如何通过REST控制器用Spring(引导)重写URL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆