Search code examples
javaspring-bootjax-rscxf

(Spring-Boot/CXF/JAXRS) @GET responds correctly with 200, @POST responds with 404 despite identical configuration


ISSUE

The issue is that my endpoints with @GET annotation work just fine and results in a 200. @POST results in a 404. They are configured almost identically. Console does not report any errors.

Background

I am setting up a controller to contain a series of GETs and POSTs. I am using SpringBoot/Tomcat. The specific library is cxf-spring-boot-starter-jaxrs (version: 3.3.3)

I followed this guide for setup: https://cxf.apache.org/docs/springboot.html

As I understand, the spring boot starter handles the servlet setup, so I don't have that to share.

Server and service bean initialization looks like this:

    @Autowired
    private List<? extends BaseController> controllers;

    @Bean
    public Server rsServer()
    {
        JAXRSServerFactoryBean endpoint = new JAXRSServerFactoryBean();

        endpoint.setBus(bus);
        endpoint.setAddress("/");
        endpoint.setServiceBeans(new ArrayList<Object>(controllers));
        endpoint.setExtensionMappings(getExtensionMapping());

        // jsonProvider() is an instance of org.apache.cxf.jaxrs.provider.json.JSONProvider
        endpoint.setProviders(Arrays.asList(jsonProvider()));

        return endpoint.create();
    }

My controller looks like this:

@Path("/v1/test")
@Service
public class SimpleTestController extends BaseController
{

    @GET
    @Path("/gettest")
    @Produces(value = { "application/json" })
    @Consumes(value = { "application/json" })
    public Response getTest()
    {
        BlankResource blankResource = new BlankResource();
        blankResource.setTest("WORKING");

        return Response.ok(gson.toJson(blankResource), MediaType.APPLICATION_JSON)
                .build();
    }

    @POST
    @Path("/posttest")
    @Produces(value = { "application/json" })
    @Consumes(value = { "application/json" })
    public Response postTest(BlankResource blankResource)
    {
        blankResource.setTest("WORKING");
        return Response.ok(gson.toJson(blankResource), MediaType.APPLICATION_JSON)
                .build();
    }
}

Note: BaseController is effectively empty, so we can ignore that.

I have also disabled all @Providers to ensure nothing is interfering. Same with spring security.

I have used both Postman and CURL to test this.

GET URL that works: http://localhost:8080/selector-service/v1/test/gettest

POST URL that does not work: http://localhost:8080/selector-service/v1/test/posttest

curl --location --request POST 'http://localhost:8080/selector-service/v1/test/posttest' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=262BEEF808419C6CEC61E064AEA3EEAA' \
--data-raw '{
    "test": true
}'

Update(12/31/2020) Another interesting note: When I run a POST on the method, it returns a 404 but when I try to GET the POST endpoint it returns a 405. This stands out to me because it appears that the server has indeed registered the post endpoint.

Update(1/4/2020) I found a blog post from someone who configured a similar setup using "cxf.jaxrs.component-scan". I followed this guide which rendered my rsServer() @Bean redundant. I have tried removing it. This had no effect and I am still having the same issue.

service.properties:

server.address=localhost

cxf.path=/
cxf.jaxrs.component-scan=true

Solution

  • Could you try mapping Spring's DispatcherServlet and CXF Servlet to different paths?

    Something like:

    # Spring MVC dispatcher servlet path. Needs to be different than CXF's to enable/disable Actuator endpoints access (/info, /health, ...)
    server.servlet-path: /
    
    # http://cxf.apache.org/docs/springboot.html#SpringBoot-SpringBootCXFJAX-RSStarter
    cxf:
      path: /api # CXFServlet URL pattern
      jaxrs:
        component-scan: true
    

    Btw, I'm the author of the blog post you are using as a reference:

    https://tech.asimio.net/2017/06/12/Implementing-APIs-using-Spring-Boot-CXF-and-Swagger.html