如何在Spring Boot中处理HTTP OPTIONS请求?

38

首先,我已经阅读了“如何处理Spring MVC的HTTP OPTIONS请求?”,但是答案似乎不直接适用于Spring Boot。

看起来我应该这样做:

通过将其dispatchOptionsRequest设置为true来配置dispatcherServlet

但是如何做到这一点,考虑到我没有XML配置或任何类型的DispatcherServlet初始化器类在我的代码中(由此答案提到)?

在一个@RestController类中,我有一个方法像这样,目前并没有被调用。

@RequestMapping(value = "/foo", method = RequestMethod.OPTIONS)
public ResponseEntity options(HttpServletResponse response) {
    log.info("OPTIONS /foo called");
    response.setHeader("Allow", "HEAD,GET,PUT,OPTIONS");
    return new ResponseEntity(HttpStatus.OK);
}

Spring Boot 1.2.7.RELEASE;一个简单的设置,与Spring REST指南中的设置并没有太大的不同。


1
在我的答案中添加了另一种解决这个问题的方法。同时,我还提交了一个PR,以更多“Spring Boot方式”(通过属性)来配置它。 - Bohuslav Burghardt
3个回答

33

选项1:Spring Boot属性(仅适用于Spring Boot 1.3.0及以上版本)

从Spring Boot 1.3.0版本开始,可以使用以下属性来配置此行为:

spring.mvc.dispatch-options-request=true

选项二:自定义 DispatcherServlet

在Spring Boot中,DispatcherServletDispatcherServletAutoConfiguration定义。您可以在配置类的某个位置创建自己的DispatcherServlet bean,它将替代自动配置中的bean:

@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setDispatchOptionsRequest(true);
    return dispatcherServlet;
}

请注意,定义自己的DispatcherServlet bean将禁用自动配置,因此您应该手动定义在自动配置类中声明的其他bean,即DispatcherServletServletRegistrationBean

选项3:BeanPostProcessor

您可以创建一个BeanPostProcessor实现,它将在初始化bean之前将dispatchOptionsRequest属性设置为true。 您可以将其放置在配置类中的任何位置:

@Bean
public DispatcherServletBeanPostProcessor dispatcherServletBeanPostProcessor() {
    return new DispatcherServletBeanPostProcessor();
}

public static class DispatcherServletBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof DispatcherServlet) {
            ((DispatcherServlet) bean).setDispatchOptionsRequest(true);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

选项4:SpringBootServletInitializer

如果您的应用程序中有SpringBootServletInitializer,您可以执行以下操作以启用OPTIONS调度:

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        servletContext.getServletRegistration(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
                .setInitParameter("dispatchOptionsRequest", "true");
    }
}

如果您将应用程序部署为WAR文件并放入Servlet容器中,那么这种方法才能起作用,因为当使用main方法运行Spring Boot应用程序时,SpringBootServletInitializer代码不会被执行。


4
重要更新:从Spring Framework 4.3版本开始,默认支持OPTIONS请求(也是即将发布的Spring Boot 1.4版本默认使用的),详见https://jira.spring.io/browse/SPR-13130。 - Sébastien Deleuze

1

您可以使用Spring Boot 2.2.6轻松将自定义方法OPTIONS添加到StrictHttpFirewall中:

@Bean
public StrictHttpFirewall httpFirewall() {

    StrictHttpFirewall firewall = new StrictHttpFirewall();
    firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST", "OPTIONS", "FOO"));

    return firewall;
}

0

我在使用基于Spring Boot 1.3.x的rest应用程序时遇到了问题,诊断问题时,我允许我的Spring Tool Suite更新到最新版本。

当我在更新后的STS中创建一个新的测试Spring Boot RestController时,它按照Spring 4.3的文档所述工作。我注意到新测试应用程序中的Maven依赖项已经跳转到spring boot 1.5.8,因此我只需更改旧应用程序的依赖项以将其更新为spring boot 1.5.8 / spring 4.3.12。这解决了问题,现在它正在使用RequestMapping注释指定对处理OPTIONS请求感兴趣的方式正常工作...

@RequestMapping(value="/account/{id}", method={RequestMethod.OPTIONS,RequestMethod.GET})

...现在将OPTIONS请求发送到处理程序。

因此,如果您能够升级到Spring的较新版本,则无需定义任何特殊配置以启用OPTIONS请求方法处理(Spring 4.3.12 / Spring Boot 1.5.8)。


1
我想这通常不是想要的。OPTIONS请求通常是CORS预检请求,它们不应该与“真实”请求完全相同。 - hgoebl
3
我同意。在控制器中处理OPTIONS不是我最终采取的做法,因为Spring的后续版本通过Crossorigin注解自动处理预检请求,这通常是我们想要的。使OPTIONS被处理的过程只是最终使用Crossorigin注解的一个步骤。顺便说一下,如果你正在为OPTIONS方法编写测试,并且想知道为什么没有返回任何CORS标头,那么你必须设置一些请求标头来触发,至少包括Origin和access-control-request-method。 - beaudet

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