415 Unsupported Media Error in PUT method using REST - java

Firstly I would like to say that I am new to development and have started working directly with REST. So I may be stupid to ask some silly questions. Please accept my apologies in advance!
Regarding the Problem - I am trying to learn how REST API WebServices can be developed so I started with a Mavan Project using the Archetype - "jersey-quickstart-webapp" and using Chrome POSTMAN as the Rest Client.
I am successful in writing GET methods but getting struck while writing PUT.
Every time I get only two things - a) Runtime Exception with 404 error or b) 415 Error with Media unsupported.
I have tried different combinations for #Consumes annotation and method parameters like #FormParam, #QueryParam but no luck. I am neither successful in reading the value sent via PUT to my application from POSTMAN nor I am able to send a simple response back to POSTMAN. Please suggest and drive me a path where I can overcome these small hurdles.
My code -
package tcs.suraj.learnwebservices;
import java.util.ArrayList;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import tcs.suraj.learnwebservices.domain.MovieBean;
#Path("/movies")
public class Movies {
static ArrayList<MovieBean> movieList = new ArrayList<MovieBean>() ;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getMovie(){
System.out.println("Under Construction");
return "Under Construction";
}
#PUT
#Consumes("application/x-www-form-urlencoded")
#Produces(MediaType.TEXT_PLAIN)
public String updateMovie(#FormParam("name") String name){
Response r ;
System.out.println(name +" updated!");
return name;
}
}

I am assuming you are using Tomcat for deployment, add this to your web.xml and then try again.
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Note: I have used org.apache.catalina.filters.CorsFilter in Tomcat 7.0.53. I am not sure what is the least version supported for CorsFilter.

Related

JSON to POJO mapping in Rest

I am new to WebServices. I was able to implement normal get and post,but when I am trying to implement post method with MediaType.APPLICATION_JSON. I am getting errors. Please let me know what is wrong here.
MY Resource :
package com;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("Hi")
public class Resource {
#GET()
#Produces(MediaType.TEXT_HTML)
public String x() {
return "HI HI";
}
#POST()
// #Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.TEXT_HTML)
public String x1() {
return "HI HI post normal";
}
#POST()
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_HTML)
public String x2(Pojo p) {
return "HI HI post jason" + p.getName();
}
}
_----------------------
My Web.xml
.......
<servlet>
<servlet-name>Bakchodi</servlet-name>
<servlet-Class>
com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Bakchodi</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>`
Pojo
package com;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="someName")
public class Pojo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Pojo[name =" + name + "]";
}
}
Jars included in pom.xml
asm,jersey{core,server,client,bundle,json,}andcommon-loggings
Please tell what I am missing.
Error:
status:415
server console:SEVERE: A message body reader for Java class com.Pojo, and Java type class com.Pojo, and MIME media type application/json was not found.
The registered message body readers compatible with the MIME media type are:
application/json ->
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$App
*/* ->
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
com.sun.jersey.core.impl.provider.entity.EntityHolderReader
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
There are couple of informations missing in your post, such as the URI structure, the version of the Servlet spec you're using, the way how you are calling the resource, ...
Anyway, you can check the following things:
You're using the annotation #POST() which is strange to me, so you should better remove the parentheses:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_HTML)
public String x2(Pojo p) {
return "HI HI post jason" + p.getName();
}
As you specified, you're expecting a POST request which means you have to send the JSON text in the body of the request, for example:
{ "name": "RAHUL" }
Unless you use an HTML form, normal browsers do not provide a way to send POST requests(at least not known to me); so you need some browser plugin to send JSON as request body; you might use Postman for Chrome or RESTClient for Firefox, ..., OR use REST client frameworks like Apache Wink, ...
After installing your favorite browser plugin, you have to set the request header Content-Type to application/json so that the framework knows you are sending a JSON body; I think this is even the reason why you're getting status:415).
Same code run (believe me , without any change) ..after my system restarted( I guess, because it caused a restart of eclipse also)..
And before it I was doing maven-clean, maven install, deletion of project from tomcat confguration at leasts 11-12 times, but was always getting this error.
So I guess this will now work for the guys who want an example for application/json in rest api.....( for sending request you can either use apps like Advanced rest client or postman , or you can create a form in html page and submit json object by changing your form by using stringify(), then submiting it..I had used ajax for this prupose)

ContainerRequestFilter method not being called

I've just started learning how to code REST web services, and I've been stuck with this for several days now. I'm coding an example application with header-based filtering, using Jersey 2 and deployed on Tomee-plus 1.7.2. No matter what I try, the ContainerRequestFilter's filter method is never called.
// TestRequestFilter.java
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
#Provider
#PreMatching
public class TestRequestFilter implements ContainerRequestFilter
{
private final static Logger log = Logger.getLogger(DemoRESTRequestFilter.class.getName());
#Override
public void filter(ContainerRequestContext requestCtx) throws IOException
{
System.out.println("FILTER-REQUEST");
}
}
My web.xml file is empty save for the required headers. The behavior right now is: the filter is recognised as #Provider and instantiated as normal, the test web service I have (just a GET returning an empty Response) can be called normally, but the filter method is never called.
Things I've tried and their effects:
Declare the filter in a class extending Application: Error on deployment.
Register the filter in a class extending ResourceConfig: Filter is instantiated twice, but filter method is still not called.
Use the classes from the com.sun.jersey.spi.container package: No effect.
Add an authentication annotation (#RolesAllowed, #PermitAll,...) to the WS method: No effect.
Add disabled=true to cfx-rs.properties in server configuration: Deployed service cannot be found at usual URL.
Add this to web.xml: No effect.
<servlet>
<servlet-name>CongressAppWS</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>org.glassfish.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.s4w.congressapp.auth.DemoRESTRequestFilter;com.s4w.congressapp.auth.DemoRESTResponseFilter</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.s4w.congressapp.auth;com.s4w.congressapp.resources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Using com.sun.jersey.spi.container prefix instead of org.glassfish.jersey.spi.container in previous code: No effect.
I'm honestly running out of options here. Every time I try something new, either there is no effect or everything stops working. Any help?
I found an alternative to ContainerRequestFilter that actually works! It's javax.servlet.Filter. Extending from this class, all I had to do was annotate it with the following code and the filtering mechanism works like a charm.
#WebFilter(filterName = "AuthenticationFilter", urlPatterns = { "/*" })
The reason for this is that ContainerRequestFilter is part JAX-RS 2.0, but TomEE 1.7.X comes with JAX-RS 1.1. You'll either have to upgrade TomEE to 7.0.0+ or use a different Server (e.g. Glassfish).

ContainerResponseFilter not executed

I want to build a REST-API with Jax-Rs with cors enabled. So I googled how to do and found this:
http://www.developerscrappad.com/1781/java/java-ee/rest-jax-rs/java-ee-7-jax-rs-2-0-cors-on-rest-how-to-make-rest-apis-accessible-from-a-different-domain/
The solution is a ResponseFilter, that adds some header information to every response, so that the browser of the user knows, that cross domain accesses are allowed.
Because the ResponseFilter is not executed when I do any request (tried GET, POST and OPTIONS), I googled again and found this:
ContainerResponseFilter not working
#lefloh gave a good answer, that sounds logic (to remove the annotation #PreMatching). It did so, but nevertheless my filter is not invoked, when I do a Http-Request.
This is my code:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class RESTServiceResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext parContainerRequestContext, ContainerResponseContext parContainerResponseContext) throws IOException {
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Origin", "*" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Credentials", "true" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Headers", "Content-Type" );
}
}
I kept on googling and found out, that I forgot to add the Filter to the web.xml. So I also did that:
<servlet>
<display-name>webinterface.api</display-name>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>de.tsystems.lbus.apprestserver</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>de.apprestserver.filter.RESTServiceResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<url-pattern>/TNR/*</url-pattern>
</servlet-mapping>
I have no more ideas now and want to ask you, if you maybe have a solution for me. Thanks in advance!
You're using Jersey 1 (can tell by the com.sun.jersey in your web.xml). The filter implementation you are using (or showing us) is Jersey 2. There's subtle difference, but it's a major difference. The latter will not work with Jersey 1.
And the fact that it even compiles (if that's the case) means that you need to get rid of some dependencies. The class you have is a JAX-RS 2 class (interface). Any JAX-RS/Jersey 2 dependency, you might have, get rid of them. They don't play well (maybe not cause of issue, but get rid of them to drop any confusion)
Jersey 1 == com.sun.jersey (keep)
Jersey 2 == org.glassfish.jersey (get rid of)
JAX-RS 2 api == javax.ws.rs-api (get rid of)
See here for Jersey 1 implementation and configuration

Using JAXB to parse JSON in Jersey

I'm trying to create a very simple RESTful web service using Jersey. I'm trying to make it so that it consumes and produces a JSON by using JAXB. The problem is that I get an error when I pass a JSON to it.
Below is the resource code. Both status() and echo() are working properly. Please note that on processRequest() I'm currently producing a text response, but that will be changed later to produce a JSON.
package web_service;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
#Path("/")
public class WebService {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String status() {
return "Web service is up and running.";
}
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.TEXT_PLAIN)
public String echo(String consumed) {
return consumed;
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String processRequest(JAXBElement<Request> r) {
Request request = r.getValue();
return "Latitude: " + request.latitude +
"\n:Longitude: " + request.longitude;
}
}
This is the Request model:
package web_service;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Request {
public String latitude;
public String longitude;
public Request() {}
public Request(String latitude, String longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
// Getters and setters for both
}
My web.xml:
<?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"
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">
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>web_service</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Finally, this is an example of the POST that I'm doing (headers set to 'Content-Type: application/json');
{
"latitude":"25.764084501106787",
"longitude":"-80.37422332275389"
}
When I run this, Tomcat gives me the following Exception:
WARNING: WebApplicationException cause:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=class web_service.Request, genericType=class web_service.Request.
And I get the following response:
415 Unsupported Media Type
Content-Length: 1 kB
Content-Type: text/html;charset=utf-8
Date: 2013 Nov 4 17:41:20
Server: Apache-Coyote/1.1
I'm very new to this and this is not making a lot of sense. Hopefully, one of you will be able to give me a hand. Thanks!
Add this to your web.xml
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
For more details look at jersey documentation : https://jersey.java.net/documentation/1.18/json.html
Try this in your endpoint:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String processRequest(Coordinates coordinates) {
return "Latitude: " + coordinates.getLatitude() +
"\n:Longitude: " + coordinates.getLongitude();
}
where Coordinates is a simple POJO mirroring the JSON content you are posting.
Then use Jackson, which has JAXB support, by adding all the libraries to your project and adding this configuration:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.foobar.rest.services;org.codehaus.jackson.jaxrs</param-value>
</init-param>
to the "Jersey REST Service" servlet.
JAXB (Java Architecture for XML Binding) avaialable in java for binding xml not for JSON.Here you are trying to convert JavaScript Object Notation with the help of xml binder in this situation and that is not possible. And i am pretty sure that you must be getting the error generated by " BodyWriter class " in the JAX-RS package....
Anyway If you want to produce and consume JSON using your resource you gonna need "moxy" JAR available in your project library for handing this conversion :)
Hope what i am writing here will be helpful for other programmers
this error happends when you are requesting without specified headers.
In your service
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
Ensure that your request gets the header "content-type" ? application/json and the header "accept" contains text/html
I think, there is problem with your Request model class. Your model class is not specifying #XmlaccessType, so by default it is being considered as PUBLIC_MEMBER. As your instance variables are also public along with getters and setters, So JAXB is not able to figure out proper bindings.That's why that exception is coming. So there are two fixes to your problem.
1)You can make your instance variables as private.
2)Or you can explicitly specify #XmlaccessType for your model class and likewise provide annotations in it which are non-conflicting.

Confusion between HttpServlet class and using it with Jersey

I am building servlets which implement a RESTful API. I understand the Jersey is a framework for deciphering and using given URL's. How do I use it in conjunction with the HttpServlet class.
I don't understand how the two work with each other. I guess this is a very broadstrokes question but I have done a fair share of reading but am still stuck on this seemingly trivial concept. I have attempted to deploy apps with classes that extend the HttpServletclass AND use Jersey annotations.
#Path("/api")
public class API extends HttpServlet{
#GET
#Path("/{name}")
#Produces("text/hmtl")
public String doGetSayHello(#PathParam("name") String name){
return "Hello" + name;
}
#GET
#Path("/articles")
#Produces("text/json")
public String doGetArticles(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
JSONObject obj = new JSONObject();
obj.put("interns", interns);
obj.put("company", "Stack Overflow");
return obj.toString();
}
}
Any help or informative materials would be greatly appreciated!
Actually you are confused because you don't understand how jersey works. Jersey framework basically uses com.sun.jersey.spi.container.servlet.ServletContainer servlet to intercept all the incoming requests. As we configure in our projects web.xml, that all the incoming rest request should be handled by that servlet. There is an init-param that is configured with the jersey servlet to find your REST service classes. REST service classes are not Servlet and they need NOT to extend the HttpServlet as you did in your code. These REST service classes are simple POJOs annotated to tell the jersey framework about different properties such as path, consumes, produces etc. When you return from your service method, jersey takes care of marshalling those objects in the defined 'PRODUCES' responseType and write it on the client stream. Here is a sample of jersey config in web.xml
<servlet>
<servlet-name>REST</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.rest.services;
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>REST</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Jersey uses a servlet to route URLs to the appropriate service. Your service itself does not need to extend a servlet.
At a high level, Jersey's ServletContainer class accepts the requests, and then based on your Jersey configuration, your web service will be invoked. You configure what url patterns are processed by Jersey. Check out section 5.3 http://www.vogella.com/articles/REST/.

Categories