3

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?

Romil Patel
  • 12,879
  • 7
  • 47
  • 76
pkgajulapalli
  • 1,066
  • 3
  • 20
  • 44
  • 1
    have you configured your server to handle CORS (Cross-Origin Resource Sharing)? Without CORS Configuration, POST will not work from a browser. However, same POST request will work from Postman/Android even without CORS configuration. – Pranjal Gore Jul 01 '19 at 12:06
  • 1
    I've configured CORS. And other POST requests are working from browser. – pkgajulapalli Jul 01 '19 at 12:35

2 Answers2

5

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
Romil Patel
  • 12,879
  • 7
  • 47
  • 76
0

Your code is specifically for a POST, so there is no handler for an OPTIONS. Spring provides a CrossOrigin annotation that implements the pre-flight CORS handler, have a read here.

Try this:

@CrossOrigin
@PostMapping("/API_PATH/{param1}/{param2}")
public Result getResult(@PathVariable Integer param1,
@PathVariable(required = false) Integer param2, @RequestBody Data data) {
    // SOME LOGIC HERE
}
stringy05
  • 6,511
  • 32
  • 38