How create a java object from CXF Webclient response - java

I'm writing a wrapper REST API (say API X) for an available REST API (say API Y) written in Apache CXF. For the wrapper I'm using CXF Webclient. This is how I call Y from X.
#GET
#Path("{userName}")
public Response getUser(#PathParam("userName") String userName) {
try {
WebClient client
= WebClient.create("https://localhost:8080/um/services/um");
Response response = client.path("users/" + userName)
.accept(MediaType.APPLICATION_JSON)
.get();
User user = (User) response.getEntity();
return Response.ok(user).build();
} catch (Exception e) {
return handleResponse(ResponseStatus.FAILED, e);
}
}
Here, User class is copied from Y to X because I can't use Y as a dependency for X. The only difference is the package name. Now when I send a request, I get a class cast exception at User user = (User) response.getEntity();.
java.lang.ClassCastException: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream cannot be cast to org.comp.rest.api.bean.User
May be that's becuase of the class package name is different?
Can someone please help me to get the response to User object?

Looks like your response is in JSON format, that right? You need to convert the stream of JSON bytes in the response to a Java Class. You are trying to cast the Stream Class to your User Class which obviously won't work. You need to parse the JSON from the data stream and then deserialize the JSON into your User Class. There are libraries that can help including Jackson and GSON
This guy has a simple example using Jackson ObjectMapper class - the ObjectMapper class has a readValue method that includes an InputStream parameter.

Jackson provider is a solution:
List<Object> providers = new ArrayList<Object>();
providers.add(new JacksonJaxbJsonProvider());
WebClient client = WebClient.create("https://localhost:8080/um/services/um", providers);
User user = client.get(User.class);

Don't need to do anything additional.
If it is a GET method
TypeOfObject response = client.path("users/" + userName)
.accept(MediaType.APPLICATION_JSON)
.get(TypeOfObject.class);
If it is a POST method
TypeOfObject response = client.path("users/" + userName)
.accept(MediaType.APPLICATION_JSON)
.post(instatanceOfTypeOfObject, TypeOfObject.class);

Related

Filtering response using webclient

I'm new using webclient to cosume a Rest API and I want to know how can I filter a response to match only what I want.
So I have this endpoint which brings me a customerById but I need to show only the the systemsId = 100
Let me show:
#GetMapping("/getCucoId/{cucoId}")
public Mono<CuCoPerson> getCucoRelationById(#PathVariable Integer cucoId) {
WebClient webClient = WebClient.create();
return webClient.get()
.uri(GET_RELATION_BY_ID + cucoId)
.header("Accept", "application/json")
.header("Authorization", "Bearer eyJraWQiOi.....")
.retrieve()
.bodyToMono(CuCoPerson.class);
}
And the POJO:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CuCoPerson {
private Integer cucoID;
private List<CustomerRelation> relatedCustomers;
}
And this is the response in Postman:
{
"cucoID": 100288298,
"relatedCustomers": [
{
"customerId": "F6305957",
"systemId": 100
},
{
"customerId": "F8364917",
"systemId": 400
},
{
"customerId": "F4194868",
"systemId": 101
}
]
}
So I need only to show the relatedCustomers who only have a systemID = 100
Thanks in advance!
I think you are looking for either Mono.map(), or Mono.flatMap().
Remove the customers, who don't match in the function, and return the changed object.
#GetMapping("/getCucoId/{cucoId}")
public Mono<CuCoPerson> getCucoRelationById(#PathVariable Integer cucoId) {
WebClient webClient = WebClient.create();
return webClient.get()
.uri(GET_RELATION_BY_ID + cucoId)
.header("Accept", "application/json")
.header("Authorization", "Bearer eyJraWQiOi.....")
.retrieve()
.bodyToMono(CuCoPerson.class)
.map(cuCoPerson -> {
List<CustomerRelation> matches = cuCoPerson.getRelatedCustomers()
.stream()
.filter(relation -> relation.getSystemId().equals(100))
.collect(Collectors.toList());
cuCoPerson.setRelatedCustomers(matches);
return cuCoPerson;
});
}
As mentioned in other answers, doing this kind of filtering on client side is bad practice. If possible the API should expose parameter for systemId and return only the data you need.
First of all this is bad Backend design. Since you (client side) have the need for such info you should have an endpoint available to get your info by customer ID and System ID. Data filtering should be done on the backend and the client should remain as thin as possible. Otherwise you perform logic both on server-side and client-side plus you send some completely unneeded info over the network and then filter it out on the client side. But if that's your available resources and you can't change the server-side then you you take your JSON response, and parse it into your class that maps to that JSON or just into Map<String,Object> if you don't have the class for that JSON and filter it out yourself. To parse a json string to map or a specific POJO you can use Jackson library - method readValue() of ObjectMapper Or Gson library. Also, I wrote my own simple wrapper over Jackson library that simplifies the use. Class JsonUtils is available as part of MgntUtils library (written and maintained by me). This class just has the methods that allow you to parse and de-serialize from/to Json String from/to a class instance. Here is a simple example on how to parse your JSON string into a Map<String,Object> with JSONUtils class
Map<String,Object> myMap
try {
myMap = JsonUtils.readObjectFromJsonString(myJsonStr, Map.class);
} catch(IOException ioe) {
....
}
MgntUtils library available as Maven artifact and on Github (including source code and Javadoc)
it could be something like this:
return webClient.get()
.uri("/someUrl")
.header("Accept", "application/json")
.header("Authorization", "Bearer eyJraWQiOi.....")
.retrieve()
.bodyToFlux(CuCoPerson.class)
.filter(cuCoPerson -> cuCoPerson.getRelatedCustomers().stream().anyMatch(cr -> cr.getSystemId() == 100))
.take(1)
.next();
But this has disadvantage that you are filtering the results on client side (your application). I would try to ask the api provider if s/he is able to provide filter parameter (something like systemId) and in that case you would call the endpoint with that query param.

How to throw exception based on feign.Response?

I have a Feign client with a method returning the feign.Response class. When another service throws an exception, feign puts an exception message on response body and puts status, but my service does not throw an exception. Can I throw an exception based on what I received in response like when I use ResponseEntity.
Feign client
#FeignClient(name = "ms-filestorage")
#RequestMapping(value = "/files", produces = "application/json")
public interface FileStorageApi {
#GetMapping(value = "/{id}")
Response getFileById(#PathVariable String id);
}
Usage of client
#Override
public Response getFileFromStorage(String fileId) {
Response fileStorageResponse = fileStorageApi.getFileById(fileId);
// NOW I USE THIS WAY FOR CHECKING RESPONSE BUT IT DOESN'T LOOK GOOD
//if (fileStorageResponse.status() != HttpStatus.OK.value()) {
// throw new OsagoServiceException();
//}
return fileStorageResponse;
}
Usually, if a Feign client call receives an error response from the API it is calling, it throws a FeignException.
This can be caught in a try / catch block (or a Feign ErrorDecoder if you want to be more sophisticated, but that's another post).
However, this is not the case if you map the error response into a Feign.Response return type - see this Github issue.
Instead of returning Feign.Response from getFileFromStorage(), you should create a custom Java object to hold the response, and you will then have access to the FeignException which you can handle as you wish.
Note that if you don't need access to the data that is returned from the API you are calling, changing the return type to void will also resolve this issue.

Can't read or parse Plain String Response from CXF REST Web Service in Java

A REST Service is implemented correctly in SpringMVC, deployed, and returns the correct result string,
#RestController
public class RESTServiceController {
#GET
#Produces("application/json")
#RequestMapping("/restUrl")
public String getResult() {
JSONObject json = new JSONObject();
json.put("result", Boolean.TRUE);
}
}
When testing this Web Service I get the correct output,
{"result":true}
The problem is the caller gets the Response object via CXF, but I don't know how to parse the Response. I don't need a custom object. All I need is the direct string, I just want to see my output string.
String restServiceURI = "http://www.asite.com/restUrl";
WebClient client = WebClient.create(restServiceURI,true);
Response resp = client.get();
//String entity = (String)resp.getEntity; <-- Also tried this to cast to a string
The issue is, the Response length is 0, Status is 302.
The getEntity InputStream brings back an EmptyInputStream based on what the debugger shows.
The Response object doesn't have any info that I can see in the debugger.
How do I just get my direct string back? Is there an example?
You are trying mix both Spring Rest and CxF rest. Either use Spring Rest or CXF Rest.
If you want to use Spring Rest as shown below
#Service
public class RESTServiceController {
#RequestMapping("/restUrl")
public #ResponseBody MyClass getResult() {
return myClass;
}
}
or CXF as shown below.
#Service
public class RESTServiceController {
#GET
#Produces("application/json")
public MyClass getResult() {
return myClass;
}
}
Note: You need not use json conversion explicitly, both Spring Rest and CXF has to feature to convert your object to json string.
However your issue doesn't stop here, I believe you've enabled spring-security, which is sending redirect(302) response, with login page. You can verify response from server by enabling logging in client side.
WebClient.getConfig(client).getInInterceptors().add(new LoggingInInterceptor());
WebClient.getConfig(client).getOutInterceptors().add(new LoggingOutInterceptor());

How to consume json parameter in java restful service

How can i consume json parameter in my webservice, I can able to get the parameters using #PathParam but to get the json data as parameter have no clue what to do.
#GET
#Path("/GetHrMsg/json_data")
#Consumes({ MediaType.APPLICATION_JSON })
#Produces(MediaType.APPLICATION_JSON)
public String gethrmessage(#PathParam("emp_id") String empid) {
}
What to use in place of #PathParam and how to parse it later.
I assume that you are talking about consuming a JSON message body sent with the request.
If so, please note that while not forbidden outright, there is a general consensus that GET requests should not have request bodies. See the "HTTP GET with request body" question for explanations why.
I mention this only because your example shows a GET request. If you are doing a POST or PUT, keep on reading, but if you are really doing a GET request in your project, I recommend that you instead follow kondu's solution.
With that said, to consume a JSON or XML message body, include an (unannotated) method parameter that is itself a JAXB bean representing the message.
So, if your message body looks like this:
{"hello":"world","foo":"bar","count":123}
Then you will create a corresponding class that looks like this:
#XmlRootElement
public class RequestBody {
#XmlElement String hello;
#XmlElement String foo;
#XmlElement Integer count;
}
And your service method would look like this:
#POST
#Path("/GetHrMsg/json_data")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void gethrmessage(RequestBody requestBody) {
System.out.println(requestBody.hello);
System.out.println(requestBody.foo);
System.out.println(requestBody.count);
}
Which would output:
world
bar
123
For more information about using the different kinds of HTTP data using JAXB, I'd recommend you check out the question "How to access parameters in a RESTful POST method", which has some fantastic info.
Bertag is right about the comment on the GET. But if you want to do POST request that consumes json data, then you can refer to the code below:
#POST
#Path("/GetHrMsg/json_data")
#Consumes(MediaType.APPLICATION_JSON)
public Response gethrmessage(InputStream incomingData) {
StringBuilder crunchifyBuilder = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(incomingData));
String line = null;
while ((line = in.readLine()) != null) {
crunchifyBuilder.append(line);
}
} catch (Exception e) {
System.out.println("Error Parsing: - ");
}
System.out.println("Data Received: " + crunchifyBuilder.toString());
// return HTTP response 200 in case of success
return Response.status(200).entity(crunchifyBuilder.toString()).build();
}
For referencing please click here
#PathParam is used to match a part of the URL as a parameter. For example in an url of the form http:/example.com/books/{bookid}, you can use #PathParam("bookid") to get the id of a book to a method.
#QueryParam is used to access key/value pairs in the query string of the URL (the part after the ?). For example in the url http:/example.com?bookid=1, you can use #QueryParam("bookid") to get the value of `bookid.
Both these are used when the request url contains some info regarding the parameters and you can use the data directly in your methods.
Please specify the problem in detail if this post doesn't help you.

Rersteasy exception "No type information to extract entity with, use other getEntity() methods"

I am using resteasy in my project. I am returning an Response object from my rest webfunction.
#Override
public Response getData(#QueryParam(value = "test") String test) {
GenericEntity<List<UserEntity>> customerentities = new GenericEntity<List<UserEntity>>(result){};// result is the List<UserEntity>
return Response.ok(customerentities).build();
}
Now in my junit test case I am doing
Response response = testService.getData("testD");
response.getEntity() // doing this to retrive the List<UserEntity>
But getting the below error
java.lang.RuntimeException: No type information to extract entity with, use other getEntity() methods
at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:334)
any idea ?
When using the getEntity() method on the Resteasy client you need to specify the type via <T> parameter, otherwise you need to call one of the other overloaded getEntity() methods that specify the expected return type in the method signature.
ClientRequest request = new ClientRequest('RESOURCE URL HERE');
ClientResponse<List<UserEntity>> response = request.get(new GenericType<List<UserEntity>>(){});
List<UserEntity> users = response.getEntity();

Categories

Resources