如何通过REST控制器用Spring引导重写URL?

如何解决如何通过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 InheritanceModifying @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.Filterorg.springframework.web.server.WebFilter实现的方式来实现。

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

  • org.springframework.http.server.ServletServerHttpResponse(非反应性)和
  • org.springframework.http.server.reactive.ServerHttpResponse(活动)

因为与Spring的HTTP请求的包装器共享org.springframework.http.HttpRequest(让我同时访问URIHttpHeaders)相反,响应的包装器没有共享一个公用接口它,所以我不得不模仿一个(此处故意以类似的方式命名,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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-