Reputation: 14953
When I configure my RequestMapping
s in Spring MVC, I'd like to automatically generate the proper Allow
header when the OPTIONS
method is used.
For example, with this controller:
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping(method = RequestMethod.GET)
ResponseEntity<String> getTest() {
return new ResponseEntity<>("test", HttpStatus.OK);
}
}
Right now if I do an OPTIONS
request to that URL I get a 405, method not allowed. Instead I'd like it to automatically respond with
Allow: GET, OPTIONS
and 204 - No content
I've got one idea adding an interceptor like so:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if("OPTIONS".equalsIgnoreCase(request.getMethod())){
response.setHeader("Allow", "GET, OPTIONS");
response.setStatus(204);
//TODO figure out the @Controller and what possible methods exist
return false;
}
return true;
}
//Deleted excess methods for brevity
});
}
Does this functionality exist without me writing a custom interceptor? If not, how might I solve the TODO
and lookup what annotations exist on the same URL the OPTIONS
call had happened on?
Upvotes: 6
Views: 8448
Reputation: 148
Anyone looking this up today, it is done this way:
@RestController
@RequestMapping(value = "/user", method = RequestMethod.OPTIONS)
public class UserRestController {
Simply defining RequestMethod.OPTIONS
in your request mapping, defines the OPTIONS for the entire controller.
Here is the reference for more info: https://docs.spring.io/spring-framework/docs/4.3.x/spring-framework-reference/htmlsingle/#mvc-ann-requestmapping-head-options
Upvotes: 0
Reputation: 14401
Changes introduced in Spring 4.3 simplified that use case. From now the OPTIONS response is automatically prepared for all mappings in your application. There is no need to manually configure the framework as the feature is available out of the box.
By default an HTTP OPTIONS request is handled by setting the "Allow" response header to the HTTP methods explicitly declared on all @RequestMapping methods with matching URL patterns. When no HTTP methods are explicitly declared the "Allow" header is set to "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"
Upvotes: 2
Reputation: 14953
To extend on Sotiros' and jhadesdev's answers. If using Java Config (like in Spring Boot) you can configure the DispatchServlet
to enable OPTIONS
request by configuring a @Bean
like so:
@Bean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet();
servlet.setDispatchOptionsRequest(true);
return servlet;
}
I then created a static helper that accepts HttpMethods varargs like so:
public static ResponseEntity<Void> allows(HttpMethod... methods) {
HttpHeaders headers = new HttpHeaders();
Set<HttpMethod> allow = new HashSet<>();
for(HttpMethod method: methods){
allow.add(method);
}
headers.setAllow(allow);
return new ResponseEntity<>(headers, HttpStatus.NO_CONTENT);
}
This makes it simple to create my own OPTIONS
mappings like so:
@RequestMapping(method = RequestMethod.OPTIONS)
ResponseEntity<Void> getProposalsOptions() {
return allows(HttpMethod.GET, HttpMethod.OPTIONS);
}
While I think it makes sense that Spring MVC could provide OPTIONS
responses automatically, you can't do it via an Interceptor
, but possibly via a custom DispatcherServlet
.
The benefit of writing your own OPTIONS
response is that it makes sense to customize the OPTIONS
in some cases based on the user's roles. For example an unauthenticated user of the API may receive Allow GET, OPTIONS
but an admin would get the full API Allow GET, PUT, DELETE, OPTIONS
You would customize the response based on examining a user's roles when making the OPTIONS
call.
Upvotes: 5
Reputation: 43087
I am not aware of a way to make it generic, this currently works although it's not generic.
set dispatchOptionsRequest to true for the dispatcher servlet in your web.xml, otherwise this prevents the servlet container to route the OPTIONS to the application:
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/mvc-dispatcher-servlet.xml</param-value>
</init-param>
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
Then adding this in the controller will return Allow: GET, OPTIONS and 204 - No content:
@RequestMapping(value = "/tryoptions", method = RequestMethod.OPTIONS)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public ResponseEntity tryOptions(HttpSession session) throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.set("Allow","OPTIONS, GET");
return new ResponseEntity(headers, HttpStatus.NO_CONTENT);
}
Upvotes: 3