JSON to POJO mapping in Rest - java

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)

Related

Java REST service generates 405 on POST

I am developing a JAX-RS REST service. I'm using Java 1.8, Tomcat 7, and invoking my POST through JQuery/AJAX.
My JQuery code is very simple:
$.post(ctx + "/addressinfo/", data, null, "json")
where data is a simple JSON object containing basic address information.
My REST controller is as follows:
package addressservice.controller;
import javax.servlet.*;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.goodyear.menuadmin.plantservice.model.PlantInfo;
import com.goodyear.menuadmin.plantservice.service.PlantInfoService;
#Path("/")
public class AddressController {
private AddressService service = new AddressService ();
private Address address;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAddress(#Context ServletContext ctx) throws Exception {
this.address= plantService.getInfo("Name");
return Response.ok(this.plant, MediaType.APPLICATION_JSON).build();
}
#POST
#Consumes(MediaType.MEDIA_TYPE_WILDCARD)
private Response saveAddress(Address data) {
boolean isOk = addressService.updateInfo(data);
if(!isOk) {
Response.serverError().build();
}
return Response.ok(data, MediaType.APPLICATION_JSON).build();
}
}
However, when I execute this, I am receiving a 405 Method Not Allowed error. The post is initiated from a button click on a form.
The GET portion works just fine.
The response headers are:
HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: HEAD,GET,OPTIONS
Content-Length: 0
Date: Mon, 10 Sep 2018 17:58:08 GMT
I've tried several different attempts to correct this ranging from adding this init parameter to my tomcat web.xml
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
I've tried changing from $.post to the following:
return $.ajax({
url: ctx + "/addressinfo/",
type: 'post',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(data)
});
EDIT
I neglected to add the servlet definition for the application's web.xml
<servlet>
<servlet-name>Address Service</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>addressservice.controller</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>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Plant Service</servlet-name>
<url-pattern>/addressinfo/*</url-pattern>
</servlet-mapping>
Nothing seems to be allowing me to post to my REST service.
Is there something I need to change in my Tomcat config? Or Am I doing something else wrong? I suppose I could change from a REST service to a servlet, But I would prefer to not have to do that.
Your Java method for #POST is private, it should be public. Jersey ignores private methods in Controllers.
saveAddress only consumes data doesn't produce
so implement
#Produces
in saveAddress as well

415 Unsupported Media Error in PUT method using REST

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.

RESTful web services using jQuery ajax

I am using restful web services using java
#Path("/data")
public class StudentDataService {
StudentInfo st=new StudentInfo();
#GET
#Path("/mydata")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Student> findAll() {
System.out.println("data returned to the web service");
return st.populateData();
}
}
I am returning a list of data containing student name,id,marks, etc.
trying to call this method with jQuery ajax as follows:
function findAll() {
console.log('findAll');
$.ajax({
type: "GET",
url: "http://localhost:9297/StudentData/rest/data/mydata",
dataType: "json",
crossDomain: true,
success: function(resp){
// we have the response
alert("Data\n '" + resp + "'");
},
error: function(e){
alert('Error: ' + e);
}
});
}
I am getting error
also it is giving error on console like "no elements found"
"getting exception: javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message body writer for Java class java.util.ArrayList, and Java type java.util.List<dao.Student>, and MIME media type application/json was not found""
You seem to missing a JSON provider. If you are using Maven, you can add this dependency
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
</dependency>
If you're not using Maven, go look for these jars
You can search for and download all the jars here
Then you can register the provider, by adding the Jackson package to your packages to scan (in your web.xml configuration)
<servlet>
<servlet-name>Jersey Web Application</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>
your.packages.to.scan,
com.fasterxml.jackson.jaxrs.json
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Use javax.ws.rs.core.Response object and build the output. Else have your own wrapper class and wrap your output and return the wrapper class back to response.
I recommend you to use gson for working with json on server side, because it's very easy to use. For example:
List<Student> items = new ArrayList<>();
while (...) {
items.add(new Student(...));
}
Gson gson = new Gson();
return gson.toJson(items);
This is just an example, adopt this to your requirements.
You might be running into a cors issue. Since you're trying to get type application/json back from the server and your request is crossDomain. If you wrap your jax-rs method with a Response object like #user1129947 suggested, you can modify the headers of the response to allow a cross-origin request like so:
import javax.ws.rs.core.Response;
#Path("/data")
public class StudentDataService {
StudentInfo st=new StudentInfo();
#GET
#Path("/mydata")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response findAll() {
return Response.ok(st.populateData())
.header("Access-Control-Allow-Origin", "*")
.build();
}
}
The header Access-Control-Allow-Origin: * will now be on the response which will tell the browser that it is allowed to receive application/json data.
Please be aware that the * parameter in the header allows any domain to access this resource
If you want to be sure that only your domains can access this resource then you must set the value of the header to a specific domain.
If you use custom headers or you end up wanting to send cookies to the server then you'll have to deal with preflighted requests which is beyond the scope of this answer.
See Mozilla's documentation on CORS for more information
Bonus Edit:
For anyone using CDI with JAX-RS you can check out this filter-interceptor combo for dealing with CORS

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.

Trying to build a REST-Service with encapsulated http response codes

I'm currently trying to build a REST webservice using Java/EE(5) that fully encapsulates the http responses, so every request should give back response code 200 (OK) and should look like this:
{
"msg" : { // imagine some datastructure here },
"error" : {
"code" : 200 // http response code
"status" : "OK" // some string defining this
}
}
My prefered framework is JAX-RS (we plan to migrate to EE6 soon, so migration is one of the topics while developing this), but can JAX-RS do this?
The easiest way to always return 200 OK and Content-Type: application/json with JAX-RS:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
#Path("/not-rest")
#Produces("application/json")
public class NotRestBean {
#GET
public Response getSoapStyle() {
String json = "{}"; // build your response here
return Response.ok(json).build();
}
}
Again, I don't recommend to do this. A central part of REST is the Uniform Interface which includes proper response codes.

Categories