HTTP Error 415 Unsupported Media Type on REST service operation with an XML parameter (Jersey + Jetty )

I developed a REST service with Jersey and Jetty. The service has two operations:

  • GET on resource test
  • POST on resource employee which consumes an Employee instance sent as an XML

When I'm calling the test operation everything works as expected, and I see on the service output the printed message. However, when I try to invoke the POST operation I get an HTTP Error 415.

To compile the project and generate the jar file I'm using maven and this is the pom.xml file content:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test.app</groupId>
    <artifactId>Service</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven-compiler-plugin.version>3.6.0</maven-compiler-plugin.version>
        <maven-shade-plugin.version>2.4.3</maven-shade-plugin.version>
        <maven-resources-plugin.version>3.0.1</maven-resources-plugin.version>
        <maven-clean-plugin.version>3.0.0</maven-clean-plugin.version>
        <maven-war-plugin.version>3.0.0</maven-war-plugin.version>
        <tomcat7-maven-plugin.version>2.1</tomcat7-maven-plugin.version>
        <maven-jaxb2-plugin.version>0.12.1</maven-jaxb2-plugin.version>
        <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>


        <jersey.version>2.26</jersey.version>
        <jetty.version>9.4.3.v20170317</jetty.version>
    </properties>

    <!-- DEPENDENCIES -->
    <dependencies>

        <!-- REST Service-->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>${jetty.version}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>${jetty.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jetty-http</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>${jetty.version}</version>
        </dependency>        
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
            <version>${jersey.version}</version>
        </dependency>
    </dependencies>

    <!-- BUILD -->
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>packaging</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <silent>true</silent>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>${maven-shade-plugin.version}</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <finalName>${project.artifactId}</finalName>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project>

And these are the code of the two classes used by the service: Service (service implementation)

package com.test.app;

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

import org.glassfish.jersey.servlet.ServletContainer;


    @Path("/api")
    public class Service {

        @GET
        @Path("test/")
        public Response test() {
            System.out.println("test invoked");
            return Response.ok().build();
        }

        @POST
        @Path("employee/")
        @Consumes(MediaType.APPLICATION_XML)
        @Produces(MediaType.APPLICATION_XML)
        public Response getEmployee(Employee employee) {
            employee.setEmployeeName(employee.getEmployeeName() + " Welcome");
            return Response.status(Status.OK).entity(employee).build();
        }

        public static void main(String[] args) throws Exception {
            ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
            context.setContextPath("/");

            int port = 46100;
            Server jettyServer = new Server(port);
            jettyServer.setHandler(context);
            //org.eclipse.jetty.util.log.Log.getRootLogger().setDebugEnabled(true);

            ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/*");
            jerseyServlet.setInitOrder(0);

            // Tells the Jersey Servlet which REST service/class to load.
            jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", Service.class.getCanonicalName());

            try {
                jettyServer.start();
                jettyServer.join();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jettyServer.destroy();
            }
        }
    }

and Employee (Object passed in as a parameter to the function)

package com.test.app;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name = "Employee")
public class Employee {

    String employeeName;

    @XmlElement
    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
}

To verify that the operation execution works as expected, I'm running the following curl command:

curl -v -XPOST localhost:46100/api/employee -H "Content-type: application/xml" -d "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Employee>
 <employeeName>Jack</employeeName>
</Employee>"

And the response that I get is an HTTP Error 415.

I suspect that I'm missing some dependency on the maven file required only at runtime that contains the conversion from the XML file to the actual Employee object.

1 answer

  • answered 2019-05-22 15:25 Sambit

    I tried your code. It is absolutely ok. I tried in Post REST Client, it is working fine. There is problem with the curl command. Find below the curl command to test.

    curl -XPOST http://localhost:46100/api/employee -H "Content-type: application/xml" -d "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Employee><employeeName>Jack</employeeName></Employee>"
    

    I did the following modifications.

    • Removed -v option
    • Added http:// in the url

    In case of RESTful webservice, you can use the following tools for testing which are really useful.