How to receive JSON Messages in POST body in a JAX-RS Restful web service in CXF? - json

I'm trying to develop a REST service using Apache-CXF, on top of JAX-RS. For starters, I have a method called test that receives a String message and int value. I want the clients to be able to pass these parameters in a POST message body. I can't seem to achieve this.
Before I paste the code here, here are some details:
I'm using CXF without Spring
It's not a web app, so I don't have the WEB-INF folder with the web.xml
I test the service using SoapUI and Postman (Google Chrome application)
With the following code, I get WARNING: javax.ws.rs.BadRequestException: HTTP 400 Bad Request:
DemoService.java
#WebService(targetNamespace = "http://demoservice.com")
#Path("/demoService")
public interface DemoService {
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test (String message, int value);
}
DemoServiceImpl.java
public class DemoServiceImpl implements DemoService {
#Override
public String test(String message, int value) {
return "test message: " + message + " value = : " + value;
}
}
DemoServer.java
public class DemoServer{
public static void main(String[] args) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
DemoService demoService = new DemoServiceImpl();
serverFactory.setServiceBean(demoService);
serverFactory.setAddress("http://localhost:9090");
serverFactory.create();
}
}
My POM.xml (minus the attributes in the root tag, everything's there)
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>demoService</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<cxf.version>3.0.0</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- Jetty is needed if you're are not using the CXFServlet -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>3.0.0-milestone1</version>
</dependency>
</dependencies>
</project>
Testing with {"message":"hello there!", "value":"50"} to the URL http://localhost:9090/demoService/test gave a HTTP 400 Bad Reuest.
Then I saw this question on S.O.: How to access parameters in a RESTful POST method and tried this:
added the following nested class in DemoServer.java:
#XmlRootElement
public static class TestRequest {
private String message;
private int value;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public int getValue() { return value; }
public void setValue(int value) { this.value = value; }
}
I also modified the DemoService interface and the implementation to use this class as a parameter in the test method, although this is still ultimately not what I want to do. (just showing the implementation here, question's already getting long):
#Override
public String test(TestRequest testRequest) {
String message = testRequest.getMessage();
int value = testRequest.getValue();
return "test message: " + message + " value = : " + value;
}
And to fix this error that I got: SEVERE: No message body reader has been found for class DemoService$TestRequest, ContentType: application/json (in Postman I see error 415 - unsupported media type) I added the following dependencies (jettison and another thing) to the POM.xml:
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>2.6.0</version>
</dependency>
I tested the service using the following JSON message, in a HTTP POST request:
{"testRequest":{"message":"hello there!", "value":"50"}}
This works. Though this solution where I use a TestRequest class to encapsulate the parameters works, that's not the solution I'm looking for. I want to be able to pass the two parameters in a JSON message, without having to introduce this TestRequest class (explicitly).
Questions:
Would this be easier to implement using Jersey?
I don't have a web.xml nor a WEB-INF folder, so I can't configure CXF in a cxf.xml file can I? A lot of tutorials online seem ot use a lot of XML configuration, but I don't want to deploy a framework like TomEE or Spring or Glassfish just to do that.
Searching online for solutions, I came across Spring Boot. Would you recommend using that, perhaps? Would that make developing web services like this easier?
Also, how do I get it to return the value in JSON format (or is it not supposed to do that for Strings?)

My friend pointed me to this stack exchange question: JAX-RS Post multiple objects
and also the following documentation: http://cxf.apache.org/docs/jax-rs-and-jax-ws.html
which states:
public class CustomerService {
public void doIt(String a, String b) {...};
}
By default JAX-RS may not be able to handle such methods as it
requires that only a single parameter can be available in a signature
that is not annotated by one of the JAX-RS annotations like
#PathParam. So if a 'String a' parameter can be mapped to a #Path
template variable or one of the query segments then this signature
won't need to be changed :
#Path("/customers/{a}")
public class CustomerService {
public void doIt(#PathParam("a") String a, String b) {...};
}
So, to answer my question, NO, it cannot be done.

Related

Rest Api Data Representation

I hava an Spring Boot Rest Api
#RestController
public class BookController {
#Autowired
private BookRepository bookRepo;
#GetMapping(value = "/library/", produces ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Book> index(){
Iterable<Book> bookIterable = bookRepo.findAll();
List<Book> bookList = new ArrayList<>();
bookIterable.forEach(a->bookList.add(a));
return bookList;
}
My Homework is to add an additonal data representation so that when i put in the request i should can choose between which data representation i won't XML or JSON
Problem is
I get even json how can i change between XML and Json when i do a get Request to the Endpoint
To solve your problem you need to use the Accept header. more details
The Content Type header indicates the type of data that you pass in the request. more details
You need to make a request with the header, if you want to send and receive xml:
Accept: application/xml;
Content-Type: application/xml;
usefull link
Ok now i found it my self what you need to know in order to use an XML output is first add
to the pom.xml file following dependencies: Jackson XML Dataformat
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Then i just have to Add Jackson Annotations to my Entity Class
#Entity
#JacksonXmlRootElement
public class Book {
public Book() {
}
#JacksonXmlProperty(isAttribute = true)
#Id
#GeneratedValue
private Integer id;
#JacksonXmlProperty(isAttribute = true)
private String title;
#JacksonXmlProperty(isAttribute = true)
private Integer numberOfCopies;
Thats it then i can Make a request with the Accept Header value application/xml

Why does response.readEntity(Post.class) return null?

I'm trying to get JSON data from REST resource and automatically convert it to Java Object with JSON-to-Java binding. I use Jersey framework 2.21 with jersey-media-moxy module as JSON provider in my client application .
I cannot figure it out why I get null instead of proper Post object when I do this:
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://www.travelportland.com/wp-json");
Response response = webTarget.path("posts/9").request().get();
Post post = response.readEntity(Post.class); // => null
The Post class implementation looks like this (at this point I only want to take 'title' field from JSON):
#XmlRootElement
public class Post {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
All works fine when I try to get String:
String postStr = response.readEntity(String.class);
or if I try to get some other resource:
WebTarget webTarget = client.target("http://jsonplaceholder.typicode.com");
Response response = webTarget.path("posts/9").request().get();
Post post = response.readEntity(Post.class); // => com.example.Post#74e28667
It seems like this issue somehow related to structure or size of JSON data.
How can I solve this problem?
Seems to be an issue with MOXy. I am not sure what (maybe size, bad chars, I don't know). But I tested with Jackson, and it works fine. You might want to just make the switch Jackson if you can't figure it out with MOXy.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
</dependency>
You will need to set the class annotation to ignore unknown properties if you are not model all the keys from the JSON
#XmlRootElement
#JsonIgnoreProperties(ignoreUnknown = true)
public class Post {
Jackson will also recognize most JAXB annotations if you already using a bunch of them.
Also make sure you get rid of the the MOXy dependency of you will have to explicitly register JacksonFeature.class with the Client, to disable the MOXy provider.

Java EE7 REST server no longer returning List<String> as JSON

The following example works in a Java EE6 (Glassfish3) project of mine but failed after I switched to Java EE7 (Glassfish4). The HTTP request returns "500 Internal Error" without any message in the Glassfish server log. The project was setup using NetBeans8 as Maven Web Project and has no special dependencies, beans.xml or other configuration.
#RequestScoped
#Path("generic")
public class GenericResource {
#GET
#Path("ping")
#Produces(APPLICATION_JSON)
public List<String> debugPing() {
return Arrays.asList("pong");
}
And then:
$ curl -v http://localhost:8080/mavenproject2/webresources/generic/ping
> GET /mavenproject2/webresources/generic/ping HTTP/1.1
...
< HTTP/1.1 500 Internal Server Error
As as I understand, all REST handling is done by the Jackson reference implementation and that Jackson uses Jersey as underlaying JSON library. One of the two is supposed to have some kind of provider for all basic data types. Only custom made classes need a self written ObjectMapper. Are these concepts still correct?
It took me some hours but I finally solved this question myself.
First fact is that the Glassfish4 JAX-RS implementation "Jersey" as switched its underlying JSON library from Jackson 1.x to Eclipselink MOXy. The latter seems not be able to convert Lists, Arrays and arbitrary POJOs to JSON out of the box. Therefore I tried to force JAX-RS to use Jackson 2.x and disable MOXy.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
// This is Jackson 2.x, Jackson 1.x used org.codehaus.jackson!
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ApplicationPath("rest")
public class RestConfig extends Application {
private final static Logger log = LoggerFactory.getLogger(RestConfig.class);
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
log.info("Enabling custom Jackson JSON provider");
set.add(new JacksonJsonProvider() /* optionally add .configure(SerializationFeature.INDENT_OUTPUT, true) */);
return set;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> map = new HashMap<>();
log.info("Disabling MOXy JSON provider");
map.put("jersey.config.disableMoxyJson.server", true);
return map;
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
// ... add your own REST enabled classes here ...
return resources;
}
}
My pom.xml contains:
<dependency>
<!-- REST (Jackson as JSON mapper) -->
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<!-- REST (Jackson LowerCaseWithUnderscoresStrategy etc.) -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
Hope this helps someone!

Bad Request 400 with Spring

I get an HTTP Bad Request when trying to reach my REST API. The issue is that Spring does not give much information to debug.
The URL is http://localhost:8080/webapp/network/v1/jobs.json?callback=jQuery203040624934318475425_1377165238418&sEcho=1&iColumns=1&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=id&sSearch=&bRegex=false&sSearch_0=&bRegex_0=false&bSearchable_0=true&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&_=1377165238419
The controller:
#Controller
#RequestMapping("/network/v1/jobs")
public class JobsController {
#RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<JQueryDatatablesPage<Job>> list(
#RequestParam int iDisplayStart, #RequestParam int iDisplayLength,
#RequestParam int sEcho, #RequestParam String search) {
...
}
}
In my pom file, I have jackson:
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
The json extension is used to return JSONP:
<filter>
<filter-name>jsonpCallbackFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>jsonpCallbackFilter</filter-name>
<url-pattern>*.json</url-pattern>
</filter-mapping>
The debugger stopped in the filter but I still get a Bad Request, and the debugger does not stop in the list method.
Stupid mistake parameter search was not in the URL. Just added #RequestParam(required = false) String search)

How to configure jackson in spring dispatcher servlet?

I want to send json data to controller in spring.How to configure jackson in dispatcher servlet and which jackson files to add in build path/lib?
You need to add the Jackson dependency first:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.7.1</version> //your version//
</dependency>
You must add #ResponseBody statement in your code as well. For example:
public class JSONController {
#RequestMapping(value="{name}", method = RequestMethod.GET)
public #ResponseBody Shop getShopInJSON(#PathVariable String name) {
Shop shop = new Shop();
shop.setName(name);
shop.setStaffName(new String[]{"mkyong1", "mkyong2"});
return shop;
}
}
Also, add < mvc:annotation-driven /> into your Spring XML configuration file.
You can find a full example of Jackson and Spring in this link.