Search code examples
javaspring-bootrestspring-mvchttp-options-method

OPTIONS Http request is failing with 404 exception while Post request is working fine


I've a rest endpoint with an optional PathVariable in spring framework.

@PostMapping("/API_PATH/{param1}/{param2}")
public Result getResult(@PathVariable Integer param1,
    @PathVariable(required = false) Integer param2, @RequestBody Data data) {
    // SOME LOGIC HERE
}

I've marked param2 Path variable as not required. So it is an optional value. This POST API works fine when I don't send any value to param2. But when the browser tries to access this API, it sends an OPTIONS type of request before sending the actuals POST request. Now if the OPTIONS reqeust doesn't contain the second path variable (param2) in the URL, it is failing with 404 exception. Is there a way to work around this problem?


Solution

  • OPTIONS requests are what we call pre-flight requests in Cross-origin resource sharing (CORS) which checks the safety measures. For the same, you may need to add CORS Configurations

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    @EnableWebMvc
    public class WebConfig implements Filter,WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**");
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
          HttpServletResponse response = (HttpServletResponse) res;
          HttpServletRequest request = (HttpServletRequest) req;
          System.out.println("WebConfig; "+request.getRequestURI());
          response.setHeader("Access-Control-Allow-Origin", "*");
          response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
          response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With,observe");
          response.setHeader("Access-Control-Max-Age", "3600");
          response.setHeader("Access-Control-Allow-Credentials", "true");
          response.setHeader("Access-Control-Expose-Headers", "Authorization");
          response.addHeader("Access-Control-Expose-Headers", "responseType");
          response.addHeader("Access-Control-Expose-Headers", "observe");
          System.out.println("Request Method: "+request.getMethod());
          if (!(request.getMethod().equalsIgnoreCase("OPTIONS"))) {
              try {
                  chain.doFilter(req, res);
              } catch(Exception e) {
                  e.printStackTrace();
              }
          } else {
              System.out.println("Pre-flight");
              response.setHeader("Access-Control-Allow-Origin", "*");
              response.setHeader("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT");
              response.setHeader("Access-Control-Max-Age", "3600");
              response.setHeader("Access-Control-Allow-Headers", "Access-Control-Expose-Headers"+"Authorization, content-type," +
              "USERID"+"ROLE"+
                      "access-control-request-headers,access-control-request-method,accept,origin,authorization,x-requested-with,responseType,observe");
              response.setStatus(HttpServletResponse.SC_OK);
          }
    
        }
    
    }
    

    Some Other Ways of CORS Configuration Using CorsRegistry:

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://domain1.com","http://domain2.com");
    }
    

    Using @CrossOrigin:

    @CrossOrigin(origins = {"http://domain1.com","http://domain2.com"})
    

    Using application.properties

    management.endpoints.web.cors.allowed-origins=http://domain1.com,http://domain2.com