Search code examples
javagradlejerseyjettyweb.xml

Why this <servlet-mapping> breaks my REST API in Jersey?


I have the following web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <display-name>jersey sample</display-name>
    <servlet>
        <servlet-name>Jersey</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>org.myproject</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

which works fine and if I go to localhost:8080/myproject/api/ping I have expected response.

But if I change "url-pattern" to "/api/*" a request to the same URL returns 404.

Do you have any idea why this can happen?

Here is the rest of my configuration.

build.gradle:

group 'myproject'
version '0.0'

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'


sourceCompatibility = 1.8


repositories {
    mavenCentral()
}


dependencies {
    compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.14'
    testCompile 'org.testng:testng:5.14.2'
}

test {
    ...
}

My code:

package org.myproject;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/api")
public class Api {
    @GET
    @Path("/ping")
    @Produces(MediaType.TEXT_PLAIN)
    public String ping() {
        return "Pong!";
    }

}

I run the application with the following command:

./gradlew clean jettyRunWar 

Solution

  • The url-pattern is the prefix or base of the servlet, in this case the Jersey servlet. It's how the servlet container knows which servlet to send the request to.

    So with /api/* You are telling the servlet container that the base of the Jersey app is /api. This base really only matters to the servlet container. Jersey handles/maps everything after it.

    So a request to /api/ping first gets the /api cut off, since it is just the app base. Then Jersey tries to locate /ping as a root resource class, meaning it should be a class annotation with @Path("ping"). Since it can't find it, you get a 404.

    So if you add the /api/*, with your current code, the request should be to /api/api/ping

    Generally you would not want to have the app base name on the resource class though. You should just have @Path("ping") on the resource class, and keep /api/* in the mapping. Just take out the @Path annotation from the ping()