我有一个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
将servlet映射到根路径时,您将覆盖DispatcherServlet
的映射,默认情况下映射到/
。
您可以尝试基本上有3种解决方案
DispatcherServlet
映射到/api
并修改控制器中的映射ServletForwardingController
将请求转发到已配置但未映射的Servlet
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。