Passing an object to a REST Web Service using Jersey - java

I have a simple WS that is a #PUT and takes in an object
#Path("test")
public class Test {
#PUT
#Path("{nid}"}
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(#PathParam("nid") WolRequest nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getId());
return response;
}
and my client side code is...
WebResource wr = client.resource(myurl);
WolResponse resp = wr.accept("application/xml").put(WolResponse.class, wolRequest);
I am trying to pass an instance of WolRequest into the #PUT Webservice. I am constantly getting 405 errors trying to do this..
How can I pass an object from the client to the server via Jersey ? Do I use a query param or the request ?
Both my POJOs (WolRequest and WolResponse) have the XMlRootElement tag defined so i can produce and consume xml..

I think the usage of the #PathParam is not correct here. A #PathParam is can basically be a String (see its javadoc for more info).
You can
use the #PathParam as a String parameter or
don't define WolRequest as a #PathParam.
The first approach:
#Path("test")
public class Test {
#PUT
#Path("{nid}")
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(#PathParam("nid") String nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid);
return response;
}
This will accept urls like: "text/12", 12 will then be the String nid. It doesn't look like this will help what you are trying to do.
The second approach:
#Path("test")
public class Test {
#PUT
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(WolRequest nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getId());
return response;
}
Your client code can be like you specified, only the url for PUT is: "test". Perhaps you need a combination of both one #PathParam for your id and one "normal" parameter to get your request data.

Check this link https://www.vogella.com/tutorials/REST/article.html
As per the code sample of method putTodo of class TodoResource ,
your code should be like this.
#Path("test")
public class Test{
#PUT
#Consumes("application/xml")
#Produces({"application/xml", "application/json"})
public WolResponse callWol(JAXBElement<WolRequest> nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getValue().getId());
return response;
}
}
Hope this will solve your problem.

You can try something like this
#POST
#Path("/post")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response callWol(WolRequest nid) {
WolResponse response = new WolResponse();
response.setResult(result);
response.setMessage(nid.getValue().getId());
return Response.status(Status.OK).entity(response).build();
}
You can try #PUT instead of #Post as well. Hope this helps

I had the same problem I solved in 3 Steps with Jackson in Netbeans/Glashfish btw.
1)Requirements :
some of the Jars I used :
commons-codec-1.10.jar
commons-logging-1.2.jar
log4j-1.2.17.jar
httpcore-4.4.4.jar
jackson-jaxrs-json-provider-2.6.4.jar
avalon-logkit-2.2.1.jar
javax.servlet-api-4.0.0-b01.jar
httpclient-4.5.1.jar
jackson-jaxrs-json-provider-2.6.4.jar
jackson-databind-2.7.0-rc1.jar
jackson-annotations-2.7.0-rc1.jar
jackson-core-2.7.0-rc1.jar
If I missed any of the jar above , you can download from Maven here http://mvnrepository.com/artifact/com.fasterxml.jackson.core
2)Java Class where you send your Post.
First ,Convert with Jackson the Entity User to Json and then send it to your Rest Class.
import com.fasterxml.jackson.databind.ObjectMapper;
import ht.gouv.mtptc.siiv.model.seguridad.Usuario;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.simple.JSONObject;
public class PostRest {
public static void main(String args[]) throws UnsupportedEncodingException, IOException {
// 1. create HttpClient
DefaultHttpClient httpclient = new DefaultHttpClient();
// 2. make POST request to the given URL
HttpPost httpPost
= new HttpPost("http://localhost:8083/i360/rest/seguridad/obtenerEntidad");
String json = "";
Usuario u = new Usuario();
u.setId(99L);
// 3. build jsonObject
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", u.getId());
// 4. convert JSONObject to JSON to String
//json = jsonObject.toString();
// ** Alternative way to convert Person object to JSON string usin Jackson Lib
//ObjectMapper mapper = new ObjectMapper();
//json = mapper.writeValueAsString(person);
ObjectMapper mapper = new ObjectMapper();
json = mapper.writeValueAsString(u);
// 5. set json to StringEntity
StringEntity se = new StringEntity(json,"UTF-8");
// 6. set httpPost Entity
httpPost.setEntity(se);
// 7. Set some headers to inform server about the type of the content
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
// 8. Execute POST request to the given URL
HttpResponse httpResponse = httpclient.execute(httpPost);
// 9. receive response as inputStream
//inputStream = httpResponse.getEntity().getContent();
}
}
3)Java Class Rest where you want to receive the Entity JPA/Hibernate .
Here with your MediaType.APPLICATION_JSON you recieve the Entity in this way :
""id":99,"usuarioPadre":null,"nickname":null,"clave":null,"nombre":null,"apellidos":null,"isLoginWeb":null,"isLoginMovil":null,"estado":null,"correoElectronico":null,"imagePerfil":null,"perfil":null,"urlCambioClave":null,"telefono":null,"celular":null,"isFree":null,"proyectoUsuarioList":null,"cuentaActiva":null,"keyUser":null,"isCambiaPassword":null,"videoList":null,"idSocial":null,"tipoSocial":null,"idPlanActivo":null,"cantidadMbContratado":null,"cantidadMbConsumido":null,"cuotaMb":null,"fechaInicio":null,"fechaFin":null}"
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.apache.log4j.Logger;
#Path("/seguridad")
public class SeguridadRest implements Serializable {
#POST
#Path("obtenerEntidad")
#Consumes(MediaType.APPLICATION_JSON)
public JSONArray obtenerEntidad(Usuario u) {
JSONArray array = new JSONArray();
LOG.fatal(">>>Finally this is my entity(JPA/Hibernate) which
will print the ID 99 as showed above :" + u.toString());
return array;//this is empty
}
..
Some tips : If you have problem with running the web after using this code may be because of the #Consumes in XML ... you must set it as #Consumes(MediaType.APPLICATION_JSON)

Try this it will work
Server Side:
#PUT
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addRecord(CustomClass mCustomClass)
{
///
///
///
return "Added successfully : "+CustomClass.getName();
}// addRecord
Client Side:
public static void main(String[] args)
{
///
///
///
CustomClass mCustomClass = new CustomClass();
Client client = ClientBuilder.newClient();
String strResult = client.target(REST_SERVICE_URL).request(MediaType.APPLICATION_XML).put(Entity.xml(mCustomClass), String.class);
}

Related

How to get a jakarta.json.JsonObject Response using the Java 11 HttpClient?

I want to fetch a jakarta.json.JsonObject in the HttpResponse itself using the Jakarta JSONP API. Right now, I have to fetch it as a String, feed the body into a reader and then get the JsonObject like the code below.
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
JsonReader jsonReader = Json.createReader(new StringReader(response.body()));
JsonObject object = jsonReader.readObject();
jsonReader.close();
How do I get the response as HttpResponse<JsonObject> response directly? I don't want to use any external libraries other than the Jakarta JSONP one.
Edit: As an example, one could write their own BodyHandler like this:
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodySubscriber;
import java.net.http.HttpResponse.BodySubscribers;
import java.net.http.HttpResponse.ResponseInfo;
import jakarta.json.JsonObject;
public class JsonObjectBodyHandler implements HttpResponse.BodyHandler<JsonObject> {
#Override
public BodySubscriber<JsonObject> apply(ResponseInfo responseInfo) {
// How to implement this
}
}
and then use it in the function like this:
HttpResponse<JsonObject> response = httpClient.send(request, new JsonObjectBodyHandler());

post gzip compressed json to jersey service

Jersey webmethod receives json and return json, i created a client to post the json with gzip encoding, i have no idea it is the right way to encode the the json to post, i want to confirm the request body is really compressed or not?
This is the jersey webmethod
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/getgzip")
public RPGZIP postDocument(RGZIP gzip){
RPGZIP _response = new RPGZIP();
_response.rpid = gzip.docid;
_response.rpdata = gzip.docdata;
return _response;
}
This is the client class to consume jersey
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import com.gzip.RGZIP;
import org.glassfish.jersey.message.GZipEncoder;
public class rclient {
public static void main(String[] args){
String REST_URI =
"http://localhost:8084/TestGZip/webresources/gzip/getgzip";
Client client = ClientBuilder.newClient();
client.register(GZipEncoder.class);
RGZIP rzip = new RGZIP();
rzip.docid = 1234;
rzip.docdata = "Working..";
Response r =
client.target(REST_URI).request(MediaType.APPLICATION_JSON)
.post(Entity.entity(rzip, MediaType.APPLICATION_JSON));
System.out.println(r.readEntity(String.class));
}
}
To know that your request is compressed, there must be a request header like Content-Encoding: gzip sent to server for server to know that they have to decompress the payload
What you did is correct in your code.

REST service always returns OK, Why?

Environment: WildFly 8.1.
I deploy 2 applications on one server. One is connecting with other and trying to run POST method on rest service.
In service project I added:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/rest")
public class RsActivator extends Application {
}
As far as I understand the user's guide for wildfly and jax-rs, this should be enough to run service listed below:
import javax.annotation.security.PermitAll;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import pl.patom.en.rest.model.LoginData;
import pl.patom.en.security.model.User;
import pl.patom.en.services.UserService;
#Path("services")
public class RestService {
#Inject
private UserService userService;
#PermitAll
#POST
#Consumes(MediaType.APPLICATION_JSON + ";charset=UTF-8")
#Path("/login")
public Response login(LoginData ld) {
User checkedUser = userService.findByLogin(ld.getUsername());
if (checkedUser == null || !checkedUser.getPassword().equals(ld.getPassword())) {
return Response.serverError().build();
}
return Response.ok().build();
}
}
In project where I call service I have this method:
public String login() {
try {
HttpClient client = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost("http://localhost:8080/en/rest/services/login/");
LoginData ld = new LoginData(username, password);
Gson gson = new Gson();
String json = gson.toJson(ld);
StringEntity input = new StringEntity(json);
input.setContentType("application/json");
postRequest.setEntity(input);
postRequest.setHeader("Authorization", "Basic cm9vdDpyb290");
HttpResponse response = client.execute(postRequest);
if (response.getStatusLine().getStatusCode() == 200) {
loggedIn = true;
return "/secured/index.xhtml?faces-redirect=true";
} else {
loggedIn = false;
}
} catch (Exception e) {
loggedIn = false;
}
return null;
}
Why do I always get 200 OK response? It doesn't matter if I use #GET or #POST annotation = always OK. Secondly, ther is no debugger activity in method (application never stops on any breakpoint within this method):
public Response login(LoginData ld)
(It works when I use RESTClient in Firefox).
Oo, and there is another curious thing... In return with this 200 OK status I always get jsf login form.
Problem solved by adding: /rest/* to non-secured section of web-resource-collection.

Testing Jersey resourse with parameters

I'm using Jersey resourse in my project, like:
#Path("/api")
public class MyResource {
#Path("/create")
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response handle(final String xml, #Context final HttpServletRequest request) {
.....
}
and I'm trying to test it:
public class MobipayResourceTest extends JerseyTest {
private MockHttpServletRequest servletRequest;
#Override
#Before
public void setUp() throws Exception {
servletRequest = new MockHttpServletRequest();
servletRequest.setMethod("POST");
}
public MobipayResourceTest() throws TestContainerException {
super("ua.privatbank.mobipay.api.resource");
}
#Test
public void testRes(){
WebResource webResource = resource();
webResource.path("/api/create").post(???); // I need to pass 2 parameters in the request - xml (in the body of post) and HttpServletRequest
}
How can I pass 2 my parameters (String xml and HttpServletRequest) to the resourse in test?
You don't need to pass the HttpServletRequest, I believe.
As to the xml parameter, I think you should have a parameter
of some other class there, not just of type String. For example
Item, Customer, Order, i.e. any business object (bean, POJO).
The way you've done it now, you'd better declare
#Consumes(MediaType.TEXT_PLAIN) because you declare that
you expect just a String in your method. Normally when
you expect XML, this XML value is unmarshalled into
an object of some type (usually a bean, POJO, etc). You
get this on the fly and you can just work with the object.
Here is some sample code of a sample Java client.
package com.company.api.test;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import com.google.gson.Gson;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.LoggingFilter;
import com.company.common.DateUtil;
import com.company.api.input.ItemOperation;
import com.company.api.input.ItemOperationData;
import com.company.api.result.ItemResult;
public class JavaClientREST {
public static void main(String[] args) throws Exception {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
client.addFilter(new LoggingFilter());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
WebResource service = client.resource(getBaseURI());
ItemOperation op1 = new ItemOperation();
op1.setItemID("447");
Date d1 = DateUtil.getDate(2013, Calendar.DECEMBER, 20);
System.out.println("DT1 = " + sdf.format(d1));
op1.setDate(d1);
op1.setOperation("pause");
String res = service.path("Item")
.entity(op1, MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_JSON)
.post(String.class);
Gson gson = new Gson();
ItemResult result = gson.fromJson(res, ItemResult.class);
System.out.println("ID = [" + result.getId() + "]");
System.out.println("Error = [" + result.getError() + "]");
System.out.println("DONE!");
}
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost:8080/api/service").build();
}
}

Jersey client: How to add a list as query parameter

I'm creating a Jersey client for a GET service that has a List as query parameter. According to the documentation, it's possible to have a List as a query parameter (this information is also at #QueryParam javadoc), check it out:
In general the Java type of the method parameter may:
Be a primitive type;
Have a constructor that accepts a single String argument;
Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String) and java.util.UUID.fromString(String)); or
Be List, Set or SortedSet, where T satisfies 2 or 3 above. The resulting collection is read-only.
Sometimes parameters may contain more than one value for the same name. If this is the case then types in 4) may be used to obtain all values.
However, I can't figure out how to add a List query parameter using Jersey client.
I understand alternative solutions are:
Use POST instead of GET;
Transform the List into a JSON string and pass it to the service.
The first one is not good, because the proper HTTP verb for the service is GET. It is a data retrieval operation.
The second will be my option if you can't help me out. :)
I'm also developing the service, so I may change it as needed.
Thanks!
Update
Client code (using json)
Client client = Client.create();
WebResource webResource = client.resource(uri.toString());
SearchWrapper sw = new SearchWrapper(termo, pagina, ordenacao, hits, SEARCH_VIEW, navegadores);
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
params.add("user", user.toUpperCase());
params.add("searchWrapperAsJSON", (new Gson()).toJson(sw));
ClientResponse clientResponse = webResource .path("/listar")
.queryParams(params)
.header(HttpHeaders.AUTHORIZATION, AuthenticationHelper.getBasicAuthHeader())
.get(ClientResponse.class);
SearchResultWrapper busca = clientResponse.getEntity(new GenericType<SearchResultWrapper>() {});
#GET does support List of Strings
Setup:
Java : 1.7
Jersey version : 1.9
Resource
#Path("/v1/test")
Subresource:
// receive List of Strings
#GET
#Path("/receiveListOfStrings")
public Response receiveListOfStrings(#QueryParam("list") final List<String> list){
log.info("receieved list of size="+list.size());
return Response.ok().build();
}
Jersey testcase
#Test
public void testReceiveListOfStrings() throws Exception {
WebResource webResource = resource();
ClientResponse responseMsg = webResource.path("/v1/test/receiveListOfStrings")
.queryParam("list", "one")
.queryParam("list", "two")
.queryParam("list", "three")
.get(ClientResponse.class);
Assert.assertEquals(200, responseMsg.getStatus());
}
If you are sending anything other than simple strings I would recommend using a POST with an appropriate request body, or passing the entire list as an appropriately encoded JSON string. However, with simple strings you just need to append each value to the request URL appropriately and Jersey will deserialize it for you. So given the following example endpoint:
#Path("/service/echo") public class MyServiceImpl {
public MyServiceImpl() {
super();
}
#GET
#Path("/withlist")
#Produces(MediaType.TEXT_PLAIN)
public Response echoInputList(#QueryParam("list") final List<String> inputList) {
return Response.ok(inputList).build();
}
}
Your client would send a request corresponding to:
GET http://example.com/services/echo?list=Hello&list=Stay&list=Goodbye
Which would result in inputList being deserialized to contain the values 'Hello', 'Stay' and 'Goodbye'
i agree with you about alternative solutions which you mentioned above
1. Use POST instead of GET;
2. Transform the List into a JSON string and pass it to the service.
and its true that you can't add List to MultiValuedMap because of its impl class MultivaluedMapImpl have capability to accept String Key and String Value. which is shown in following figure
still you want to do that things than try following code.
Controller Class
package net.yogesh.test;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import com.google.gson.Gson;
#Path("test")
public class TestController {
#Path("testMethod")
#GET
#Produces("application/text")
public String save(
#QueryParam("list") List<String> list) {
return new Gson().toJson(list) ;
}
}
Client Class
package net.yogesh.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.ws.rs.core.MultivaluedMap;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.core.util.MultivaluedMapImpl;
public class Client {
public static void main(String[] args) {
String op = doGet("http://localhost:8080/JerseyTest/rest/test/testMethod");
System.out.println(op);
}
private static String doGet(String url){
List<String> list = new ArrayList<String>();
list = Arrays.asList(new String[]{"string1,string2,string3"});
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
String lst = (list.toString()).substring(1, list.toString().length()-1);
params.add("list", lst);
ClientConfig config = new DefaultClientConfig();
com.sun.jersey.api.client.Client client = com.sun.jersey.api.client.Client.create(config);
WebResource resource = client.resource(url);
ClientResponse response = resource.queryParams(params).type("application/x-www-form-urlencoded").get(ClientResponse.class);
String en = response.getEntity(String.class);
return en;
}
}
hope this'll help you.
One could use the queryParam method, passing it parameter name and an array of values:
public WebTarget queryParam(String name, Object... values);
Example (jersey-client 2.23.2):
WebTarget target = ClientBuilder.newClient().target(URI.create("http://localhost"));
target.path("path")
.queryParam("param_name", Arrays.asList("paramVal1", "paramVal2").toArray())
.request().get();
This will issue request to following URL:
http://localhost/path?param_name=paramVal1&param_name=paramVal2
GET Request with JSON Query Param
package com.rest.jersey.jerseyclient;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
public class JerseyClientGET {
public static void main(String[] args) {
try {
String BASE_URI="http://vaquarkhan.net:8080/khanWeb";
Client client = Client.create();
WebResource webResource = client.resource(BASE_URI);
ClientResponse response = webResource.accept("application/json").get(ClientResponse.class);
/*if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}
*/
String output = webResource.path("/msg/sms").queryParam("search","{\"name\":\"vaquar\",\"surname\":\"khan\",\"ext\":\"2020\",\"age\":\"34\""}").get(String.class);
//String output = response.getEntity(String.class);
System.out.println("Output from Server .... \n");
System.out.println(output);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Post Request :
package com.rest.jersey.jerseyclient;
import com.rest.jersey.dto.KhanDTOInput;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
public class JerseyClientPOST {
public static void main(String[] args) {
try {
KhanDTOInput khanDTOInput = new KhanDTOInput("vaquar", "khan", "20", "E", null, "2222", "8308511500");
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put( JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
// final HTTPBasicAuthFilter authFilter = new HTTPBasicAuthFilter(username, password);
// client.addFilter(authFilter);
// client.addFilter(new LoggingFilter());
//
WebResource webResource = client
.resource("http://vaquarkhan.net:12221/khanWeb/messages/sms/api/v1/userapi");
ClientResponse response = webResource.accept("application/json")
.type("application/json").put(ClientResponse.class, khanDTOInput);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code :" + response.getStatus());
}
String output = response.getEntity(String.class);
System.out.println("Server response .... \n");
System.out.println(output);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Categories