【问题标题】:How to declare Servlet on root path without overriding Spring MVC Controllers如何在不覆盖 Spring MVC 控制器的情况下在根路径上声明 Servlet
【发布时间】:2019-03-19 19:01:25
【问题描述】:

我的 Spring Boot 应用程序带有映射到 /api 的 REST API。我需要在/ 上定义额外的servlet。我希望与 /api 匹配的所有请求都由 REST API 处理,而所有其他请求都由 servlet 处理。如何做到这一点?

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @RestController
    @RequestMapping("/api")
    public class ApiController {

        @GetMapping
        public String get() {
            return "api";
        }
    }

    @Bean
    public ServletRegistrationBean customServletBean() {
        return new ServletRegistrationBean<>(new HttpServlet() {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
                resp.getWriter().println("custom");
            }
        }, "/*");
    }   
}

在上面的代码中,我想要这样的东西:

curl  http://localhost:8080/api/                                                                                                                           
> api⏎       
curl  http://localhost:8080/custom/
> custom

我尝试使用过滤器重定向请求,但所有请求都转到自定义 servlet:

@Bean
public FilterRegistrationBean apiResolverFilter() {
    final FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter((req, response, chain) -> {
        HttpServletRequest request = (HttpServletRequest) req;
        String path = request.getRequestURI().substring(request.getContextPath().length());
        if (path.startsWith("/api/")) {
            request.getRequestDispatcher(path).forward(request, response);
        } else {
            chain.doFilter(request, response);
        }
    });
    registrationBean.addUrlPatterns("/*");
    return registrationBean;
}

此项目在 github 上可用:https://github.com/mariuszs/nestedweb

【问题讨论】:

  • 看看这个帖子 stackoverflow.com/questions/3125296/…>
  • @Gro 我试过了,这不起作用(或者我不知道如何使用它)
  • 那行不通。 DispatcherServlet 映射到/ 注册另一个将破坏DispatcherServlet。为什么它需要是一个 servlet,为什么不只是一个普通的控制器?你想达到什么目的?
  • @M.Deinum 我需要使用来自外部库 (GitHttpServlet) 的现有 servlet
  • 不破坏DispatcherServlet。您可以尝试使用ServletWrappingController,使其位于DispatcherServlet 后面。

标签: java spring spring-boot servlets


【解决方案1】:

将 servlet 映射到根路径时,您将覆盖 DispatcherServlet 的映射,默认情况下,该映射映射到 /

基本上有 3 种解决方案您可以尝试

  1. DispatcherServlet 映射到/api 并修改控制器中的映射
  2. 使用ServletForwardingController 将请求转发到已配置但未映射的Servlet
  3. 使用ServletWrappingController 包装Servlet 实例

编号 2 和 3 几乎相同,不同之处在于,使用选项 3 Spring 还管理 Servlet 实例,而使用选项 2,Servlet 容器管理 Servlet

DispatcherServlet 映射到/api

如果您的所有控制器都映射在/api 下,则选项 1 可以是一个选项,如果不是,这不是一个选项。在您的application.properties 中,您可以将spring.mvc.servlet.path 设置为/api。然后,您将像在问题中所做的那样配置您的其他 Servlet

使用ServletForwardingController

Spring 提供了一个ServletForwardingController,它将在给定servlet 名称的ServletContext 中查找Servlet,并将请求转发给它。您仍然必须注册Servlet,但要防止它被映射。

接下来您需要SimpleUrlHandlerMapping 将 URL 映射到此控制器或将其设置为默认处理程序(基本上是包罗万象)。

@Bean
public ServletForwardingController forwarder() {
   ServletForwardingController controller = new ServletForwardingController();
   controller.setServletName("my-servlet");
   return controller;
}

@Bean
public CustomServlet customServlet() {
    return new CustomServlet();
}

@Bean
public ServletRegistrationBean customServletRegistration() {
    ServletRegistrationBean registration = new ServletRegistrationBean(customServlet(), false);
    registration.setServletName("customServlet");
    return registration;
}

@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
    SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
    mapping.setDefaultHandler(forwarder());
    mapping.setOrder(LOWEST_PRECEDENCE  - 2);
    return mapping;
}

使用ServletWrappingController

Spring 提供了一个ServletWrappingController,它将在内部创建和配置一个Servlet 实例。它充当从/到 Servlet 到 Spring Controller 的适配器。在这种情况下,您不必注册CustomServlet,因此配置ServletForwardingController 会稍微容易一些。

接下来您需要SimpleUrlHandlerMapping 将 URL 映射到此控制器或将其设置为默认处理程序(基本上是包罗万象)。

@Bean
public ServletWrappingController wrapper() {
   ServletWrappingController  controller = new ServletWrappingController ();
   controller.setServletName("my-servlet");
   controller.setServletClass(CustomerServlet.class);
   return controller;
}

@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
    SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
    mapping.setDefaultHandler(wrapper());
    mapping.setOrder(LOWEST_PRECEDENCE  - 2);
    return mapping;
}

根据您的架构和 url 结构,您可能希望选择选项 1 或选项 3。

【讨论】:

    猜你喜欢
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多