I am developing the REST application using Jersey and creating CRUD operations in my service.
My question is how to bind the JSON as an object in method. I am not able to doing the save operation using JSON request.
So please provide me any useful example which have developed the CRUD application.
Jersey JSON support comes as a set of extension modules such as MOXy and Jackson.
MOXy
JSON binding support via MOXy is a default way of supporting JSON binding in your Jersey applications since Jersey 2.0. When JSON MOXy module is on the classpath, Jersey will automatically discover the module and enable JSON binding support via MOXy in your applications.
To use MOXy as your JSON provider you need to add jersey-media-moxy module to your pom.xml file:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.22.1</version>
</dependency>
If you're not using Maven make sure to have all needed dependencies on the classpath.
With MOXy, you can use JAXB annotations to control how the JSON is generated.
Jackson 2.x
To use Jackson 2.x as your JSON provider you need to add jersey-media-json-jackson module to your pom.xml file:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22.1</version>
</dependency>
If you're not using Maven make sure to have all needed dependencies on the classpath.
Jackson 2.x provides a rich set of annotations to control how the JSON is generated from your POJOs.
Creating a CRUD application
Define a POJO which will be marshaled from/to JSON. Consider the following class as an example:
public class Book implements Serializable {
private Long id;
private String title;
private String description;
public Book {
}
// getters and setters ommited
}
Depending on your JSON provider, you can use annotate your POJO to control how the JSON is generated.
Create your REST endpoint. To consume data as JSON, simply annotate it with #Consumes(MediaType.APPLICATION_JSON). To produce JSON, annotate it with #Produces(MediaType.APPLICATION_JSON).
Consider the following class as an example to start:
#Path("/books")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class BookEndpoint {
#POST
public Response create(Book book) {
...
}
#GET
#Path("/id")
public Response read(#QueryParam("id") Long id) {
...
}
#PUT
#Path("/id")
public Response update(#QueryParam("id") Long id, Book book) {
...
}
#DELETE
#Path("/id")
public Response delete(#QueryParam("id") Long id) {
...
}
}
In Jersey use #Consumes and #Produces to customize requests and responses.
Example: We have a Car object .
public class Car {
private Long id;
private String color;
private int maxSpeed;
private String manufacturer;
//...
//Getter and Setter
}
RESTful Service:
#Path("cars")
#Produces(MediaType.APPLICATION_JSON)
public class CarAPIService {
//...
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("save")
public Car save(Car instanceToSave) {
//Implement method
}
}
Yo can use any REST client to test this, as, for example, Postman.
Maybe you could use:
http://docs.spring.io/spring-framework/docs/2.5.x/api/org/springframework/beans/BeanUtils.html
or
https://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/BeanUtils.html
BeanUtils.copyProperties(source, target);
Related
I am implementing a rest-full Web service with Jersey which accepts http request from client in json form.
On fortify scan I am getting critical issue :-"mass assignment insecure binder configuration".
I want to bind the json values in htttp request to the model class in my server side code and since it is a small module I want to avoid using Spring MVC framework.
Below is my code snippet which is working fine but I need to map json request to model class below without using Spring MVC.
#POST
#Path("/TimRestService")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response crunchifyREST**(JsonObject model**, #Context HttpServletRequest request) {
System.out.println(model);
return Response.status(200).entity(model).build();
}
This is the model class :-
public class ActivateService {
public String mWalletToken;
public String topMerchantEMPID;
public String serviceCategory;
}
I checked these links , however the answer is more specific to Spring MVC fmwrk:
What is the solution for Mass Assignment: Insecure Binder Configuration Vulnerability?
How to fix Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) in java
This can be implemeted via Jacksonson . Jackson is one of the best JSON Providers/parsers and can be used with Jersey in Rest implemetation.The REST services will produce and consume JSON and the JSON serialization and de-serialization happens automatically behind the scenes
Create View class as :
public class View {
public static class Editable {}
public static class Viewable extends Editable {}
public static class Internal extends Viewable {}
}
Create Model class as :
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "activateService")
public class ActivateService implements Serializable {
#JsonView(View.Editable.class)
public String mWalletToken;
#JsonView(View.Editable.class)
public String topMerchantEMPID;
#JsonView(View.Editable.class)
public String serviceCategory;
}
and the Rest -full web service method :
#POST
#Path("/TimRestService")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response crunchifyREST(#JsonView(View.Editable.class) final ActivateService model, #Context HttpServletRequest request) {
In JAX-RS, if one model(either request or response) is annotated with #JsonView(View.Editable.class), in our case add method, Jackson will only serialize or deserialize fields that are annotated with #JsonView(View.Editable.class).
In our case, client can only pass editableField, if client pass any other fields, server will just silently ignore them.
Use below dependencies in pom.xml
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-
databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-json -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.19.4</version>
</dependency>
Source :- https://www.owasp.org/index.php/Mass_Assignment_Cheat_Sheet
and http://lifelongprogrammer.blogspot.com/2015/09/using-jackson-view-to-protect-mass-assignment.html
it also works just by placing this in the pojo or model class
#JsonIgnoreProperties(ignoreUnknown=true)
public class ActivateService {
[...]
}
resource:
https://stackoverflow.com/a/39013609/8782229
I am trying to test a Jax-rs resource by following this https://jersey.java.net/documentation/latest/test-framework.html,
and I am using container jersey-test-framework-provider-jdk-http
I can assert status code. However, when I try to readEntity, I get exception:
javax.ws.rs.ProcessingException: Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
at org.jboss.resteasy.core.interception.ClientReaderInterceptorContext.throwReaderNotFound(ClientReaderInterceptorContext.java:39)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:73)
at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:50)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:248)
at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readEntity(ClientResponse.java:181)
at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:217)
My Resource Class:
#Path("/")
public class SampleResource {
#GET
#Path("/health")
#Produces(MediaType.APPLICATION_JSON)
public String getServiceStatus() {
return "{\"Status\": \"OK\"}";
}
}
My Test Class:
public class TestSampleResource extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class);
}
#Test
public void testHealthEndpoint() {
Response healthResponse = target("health").request(MediaType.APPLICATION_JSON).get();
Assert.assertEquals(200, healthResponse.getstatus()); // works
String body = healthResponse.readEntity(String.class);
Assert.assertEquals("{\"Status\": \"OK\"}", body);
}
}
Can anyone please help?
The problem comes from having both Jersey and RestEasy client on the classpath. When you call target() on the JerseyTest, the WebTarget is obtained from a Client that is built by calling ClientBuilder.newClient().
The ClientBuilder is a standard JAX-RS API, and it is implemented first to search for an implementation of ClientBuilder through the META-INF/services files, looking for a file named javax.ws.rs.client.ClientBuilder, whose content is the name of an implementation of the ClientBuilder. If no such file is found, it defaults to looking for JerseyClientBuilder.
jersey-client has no such file META-INF/services/javax.ws.rs.core.ClientBuilder because it's ClientBuilder is the default for JAX-RS client. If you look in your resteasy-client jar, you will see the it does have that file. And if you look in the contents of that file, you will see the ResteasyClientBuilder as the implementation.
So even though you are using Jersey's test framework, the Client being used, is RESTeasy's implementation. And I guess all the standard configurations with entity providers never gets configured. Conversion between String and application/json is one of those standard providers you need in your case.
I would say just explicitly use Jersey client implementation. You will no longer be able to call target on the JerseyTest. You will need to explicitly create the client
#Test
public void dotest() {
final Client client = new JerseyClientBuilder().build();
WebTarget target = client.target("http://localhost:9998");
final Response response = target.path("health").request().get();
final String json = response.readEntity(String.class);
}
The default base path for JerseyTest is http://localhost:9998, so I explicitly create the WebTarget with that.
Note that I said the String to/from application/json is supported by standard providers. So if you will only be serializing Strings, then you don't need anything else. If you want POJO serialization support for the Jersey client (and server side), you should add the following
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
<scope>test</scope>
</dependency>
I suspect the json parser in your test is being misguided by the presence of curly braces. Basically it thinks you are returning a json object, not a json string. Try returning "Status:OK"
As the exception says you are missing a MessageBodyReader for content-type application/json. Do you have JacksonJsonProvider on your classpath? It can be added as a dependency to jackson-jaxrs-json-provider:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.3</version>
</dependency>
Then register the JacksonJsonProvider in your test application:
#Override
protected Application configure() {
return new ResourceConfig(SampleResource.class, JacksonJsonProvider.class);
}
I have a field annotated with #NotNull, when I pass JSON to my Rest resource that is missing that field, it just carries on.
From my understanding of the documentation, I don't need anything else than what I've got to do simple validation.
As far as I can tell, I have everything in place, not sure what I'm missing. Using Java 8, Jersey 2.8, maven tomcat7 plugin.
#Path("/trade")
public class TradeEndpoint {
#Autowired
private OrderFillService orderFillService;
#POST
#Path("limit")
#Consumes({ MediaType.APPLICATION_JSON })
public void limitOrder(#Valid LimitOrderModel limitOrder) {
placeTrade(limitOrder);
}
...
}
public class LimitOrderModel {
#NotNull
private String symbol;
...
}
I set the property to send errors back to the client:
public Application () {
packages("com.foo.web.endpoint");
register(JacksonFeature.class);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, "true");
}
I have a dependency on jersey-bean-validation:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.21</version>
</dependency>
Posting this JSON works (placeTrade() is executed):
{
"limitPrice":"60",
"side":"SELL",
"quantity":"100"
}
Found the problem. I'm on Jersey 2.8 (makes life with jackson easier) but I was depending on jersey-bean-validation 2.21. Dropping down to jersey-bean-validation 2.8 works a treat.
Jersey 1.18.1 here. I have the following Jersey resource defined on my server:
#Path("/location")
#Produces(MediaType.APPLICATION_JSON)
public class LocationResourceImpl implements LocationResource {
private ObjectMapper mapper;
public LocationResourceImpl() {
super();
mapper = new ObjectMapper();
// TODO: Configure for JSON POJO mapping how?!?
}
#GET
#Path("address/{address_id}")
#Override
public Address getAddress(#PathParam("address_id") Long id) {
Address address;
address = new Address(
1L,
"19 ABC Dr",
"Suite 3",
"Testville",
"NY",
"US",
"12345");
return address;
}
}
My Address POJO is properly annotated with #JsonProperty annotations. I am trying to figure out how to configure my ObjectMapper instance so that the Address instance returned by getAddress(Long) returns my address as JSON.
Any ideas as to what I can do? It look like setSerializationConfig and setDeserializationConfig methods were added in Jersey 2.x, but upgrading isn't an option for me, as I'm using DropWizard 0.7.1, which depends on Jersey 1.18.x.
With Dropwizard, we don't need any special configuration for basic Jackson POJO mapping support. As explained in the Dropwizard User Guide: How it's Glued Together:
When your application starts up, it will spin up a Jetty HTTP server, see DefaultServerFactory. This server will have two handlers, one for your application port and the other for your admin port.
The application port has an HttpServlet as well, this is composed of DropwizardResourceConfig, which is an extension of Jersey’s resource configuration that performs scanning to find root resource and provider classes.
DropwizardResourceConfig is where the various ResourceMethodDispatchAdapter are registered to enable the following functionality:
Enables using Jackson to parse request entities into objects and generate response entities from objects, all while performing validation.
I have 2 questions:
1. Can I create one class, annotate it with JAXB annotations(for XML support) and declare in web.xml
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
for JSON (Jackson library) support? Or I need to create separately two classes for JSON and XML?
Or may be exist some more elegant way to cause REST service to return both JSON and XML?
2. How I can programmatically choose what type to return (JSON or XML)?
Thanks.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Can I create one class, annotate it with JAXB annotations(for XML support) and declare in web.xml for JSON (Jackson library) support?
You can always use an Application class to specify a MessageBodyReader/MessageBodyWriter for the JSON binding. I believe Jackson provides an implementation in its jar. Below is an example of an Application class that specifies MOXy as the JSON provider:
package org.example;
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class CustomerApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(CustomerService.class);
return set;
}
}
Or I need to create separately two classes for JSON and XML?
EclipseLink JAXB (MOXy) offers native XML binding and is designed to enable you to use the same object model for both JSON and XML. You can integrate it into your JAX-RS application using the MOXyJsonProvider class:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
How I can programmatically choose what type to return (JSON or XML)?
Server Side
You can specify that your service offers both XML and JSON messages using the #Produces annotation.
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("{id}")
public Customer read(#PathParam("id") long id) {
return entityManager.find(Customer.class, id);
}
For More Information
http://blog.bdoughan.com/2012/03/moxy-as-your-jax-rs-json-provider.html
Client Side
You can use the MediaType to indicate the type of message. Below is an example using Jersey client APIs. Note how the URL is the same, just the requested media type is different.
Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/CustomerService/rest/customers");
// Get XML response as a Customer
Customer customer = resource.path("1")
.accept(MediaType.APPLICATION_XML)
.get(Customer.class);
System.out.println(customer.getLastName() + ", "+ customer.getFirstName());
// Get JSON response as a Customer
Customer customer = resource.path("1")
.accept(MediaType.APPLICATION_JSON)
.get(Customer.class);
System.out.println(customer.getLastName() + ", "+ customer.getFirstName());
For More Information
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-55.html
If your client wants to use a part of the URL to configure the response type, you can use a Servlet filter.
An easy way to implement overriding the representation (media type) could use a URL query parameter:
/resources/todo?format=json
The Servlet filter parses the URL query parameters, and if a format=json is present, replaces or adds the accept header "application/json".
No need for seperate classes, what you need is seperate methods:
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Todo getXML() {
Todo todo = new Todo();
todo.setSummary("This is my first todo");
todo.setDescription("This is my first todo");
return todo;
}
Then in the client side, when you request for the service, you indicate in what format you want it:
// Get XML
System.out.println(service.path("rest").path("todo").accept(MediaType.TEXT_XML).get(String.class));
// Get XML for application
System.out.println(service.path("rest").path("todo").accept(MediaType.APPLICATION_XML).get(String.class));
// Get JSON for application
System.out.println(service.path("rest").path("todo").accept(MediaType.APPLICATION_JSON).get(String.class));