如何在不覆盖Spring MVC控制器的情况下在根路径上声明Servlet

3

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

最初的回答:

您可以使用Spring Boot中的ServletRegistrationBean来注册您的servlet,并将其映射到/上。然后,您可以使用RequestMapping和PathVariable注释来指定哪些请求应该由REST API处理。

@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: "最初的回答"

我尝试使用过滤器来重定向请求,但所有请求都会进入自定义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,原始回答的意思是这个链接指向最初的答案。

1
请查看此线程:https://dev59.com/hHA75IYBdhLWcg3wv77A - Gro
@Gro 我已经尝试过了,但这并不起作用(或者我不知道如何使用它)。 - MariuszS
1
那样做行不通。DispatcherServlet 已经映射到 /,注册另一个将会破坏 DispatcherServlet。为什么它需要是一个 servlet,而不是一个普通的控制器?你想要实现什么? - M. Deinum
为什么它需要映射到“/”?你也可以将该servlet包装在“`”中。 - M. Deinum
1
不使用DispatcherServlet是不可能的。您可以尝试使用ServletWrappingController将其放置在DispatcherServlet之后。 - M. Deinum
显示剩余2条评论
1个回答

3
当将servlet映射到根路径时,您将覆盖默认映射到/DispatcherServlet的映射。
基本上有三种解决方案:
  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,它将查找 ServletContext 中的 Servlet 并将请求转发给它。您仍然需要注册 Servlet,但要防止它被映射。

接下来,您需要一个 SimpleUrlHandlerMapping 将 URL 映射到该控制器或将其设置为默认处理程序(基本上是一个 catch-all)。

@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。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接