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

问题描述 投票:0回答:1

我有一个Spring引导应用程序,其中REST API映射在/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

java spring spring-boot servlets
1个回答
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中查找给定servlet名称的ServletContext并将请求转发给它。你仍然需要注册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。

© www.soinside.com 2019 - 2024. All rights reserved.