How to parse Response from WebService call in Play 2.3.x - java

Background : I'm calling backend WebServices from Play controller and sending the Response (in JSON format) to AngularJS module wrapped in play.mvc.Result. This integration works seamlessly.
Problem Statement : Now I want to parse the Response and use it for some business logic; but play.mvc.Result class has only one method which is toScala(). How do I get the body of play.mvc.Result.
Can I use play.libs.F.Promise to get my job done?
Below is the Generalized code which takes JSON request body and Service URL as parameter and returns the play.mvc.Result.
WSRequestHolder requestHolder = WS.url("https://application.url/ws/endpoint")
.setHeader("Content-Type", "application/json");
final Promise<String> promise = requestHolder.post(jsonRequest)
.map(new Function<WS.Response, String>() {
#Override
public String apply(final Response a) throws Throwable {
//Do i need to Parse from here???
return a.getBody();
}
});
return Results.async(promise.map(new Function<String, Result>() {
#Override
public Result apply(final String a) throws Throwable {
if(STR_UNAUTHORIZED.equals(a)){
return Results.redirect(controllers.routes.Authentication.login("",""));
}
return Results.ok(a);
}
}));
So is there a way to extract the Response body from play.mvc.Result or is there any alternate way to do this?

Below code would Parse the response from WebService call in synchronized way:
WSRequestHolder requestHolder = WS.url("https://application.url/ws/endpoint")
.setHeader("Content-Type", "application/json");
final Promise<WS.Response> promise = requestHolder.get();
Response myResponse=promise.get(50000);
// This code returns the Parsed response in form of String
return myResponse.getBody();

Related

Micronaut HttpResponse body CompositeByteBuf type throws io.netty.util.IllegalReferenceCountException: refCnt: 0

I am using Micronaut #Client to call external service which returns me response
of type FullNettyClientHttpResponse and it has body in the form of CompositeByteBuf(freed, components=1); I want to convert CompositeByteBuf to a human readable toString message but it has failing with IllegalReferenceCountException. Please provide suggestion how I can get the text message here.
#Client(value = "url")
public interface MyClient {
#Post(consumes = MediaType.APPLICATION_XML, produces = MediaType.APPLICATION_XML)
HttpResponse call(String body);
}
class service{
void method(){
HttpResponse httpResponse = client.call(request);// returns FullNettyClientHttpResponse with body "Optional[CompositeByteBuf(freed, components=1)]"
Optional<CompositeByteBuf> reBody = httpResponse.getBody(CompositeByteBuf.class);
if(reBody.isPresent()){
CompositeByteBuf b=reBody.get();
byte[] req = new byte[b.readableBytes()];
b.readBytes(req);
String body = new String(req, CharsetUtil.UTF_8).substring(0, req.length -
System.getProperty("line.separator").length());
System.out.println("server receive order : " + body);
}
}
I tried to get the message using toString but failed with IllegalReferenceCountException.
b.toString(Charset.defaultCharset()); // Method threw 'io.netty.util.IllegalReferenceCountException' exception.
toString returns CompositeByteBuf.
b.toString(); //CompositeByteBuf(freed, components=1);
You must specify the body type in the client if you want micronaut to keep the body of the response.
For example:
HttpResponse<String> call(String body);

How to code restcontroller for google actions?

I wish to code the Rest Controller in spring-boot for my webhook. I am creating a google action, with simple actions.
This is a boilerplate: https://github.com/actions-on-google/dialogflow-webhook-boilerplate-java/blob/master/src/main/java/com/example/ActionsServlet.java.
I want to do the same, only in spring-boot. I want to manipulate JSON body as input, but not sure how to do this.
#RestController
public class indexController extends HttpServlet {
#Autowired
private App actionsApp;
//handle all incoming requests to URI "/"
// #GetMapping("/")
// public String sayHello() {
// return "Hi there, this is a Spring Boot application";}
private static final Logger LOG = LoggerFactory.getLogger(MyActionsApp.class);
//handles post requests at URI /googleservice
#PostMapping(path = "/", consumes = "application/json", produces = "application/json")
public ResponseEntity<String> getPost(#RequestBody String payload,
#RequestHeader String header, HttpServletResponse response) throws IOException {
//Not sure what to do here.
System.out.println(jsonData);
return ResponseEntity.ok(HttpStatus.OK);
try {
//writeResponse(response, jsonResponse);
//String med request body og object that has all request header entries
String jsonResponse = actionsApp.handleRequest(body, listAllHeaders(header)).get();
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
} catch (
InterruptedException e) {
System.out.println("Something wrong happened, interupted");
} catch (
ExecutionException e) {
System.out.println("Something wrong happened, execution error");
}
}
First, there is an error in your code. There might be a wrong "return" before your function logic.
return ResponseEntity.ok(HttpStatus.OK);
Second, as you are using Spring Framework, and you use "#RequestBody String payload" in the method, the Spring Framework will take the request body and set it to payload. If you set payload as a specific type. The framework will deserialize the body to it.
Finally, you can directly use payload in your code. The value of it would be the request body.
If you want to decode the json string. You can use org.json library.
JSONObject obj = new JSONObject(payload);
String name = obj.optString("name");
The code will get the value of name in the json.

Reactive WebClient GET Request with text/html response

Currently I’m having an issue with new Spring 5 WebClient and I need some help to sort it out.
The issue is:
I request some url that returns json response with content type text/html;charset=utf-8.
But unfortunately I’m still getting an exception:
org.springframework.web.reactive.function.UnsupportedMediaTypeException:
Content type 'text/html;charset=utf-8' not supported. So I can’t
convert response to DTO.
For request I use following code:
Flux<SomeDTO> response = WebClient.create("https://someUrl")
.get()
.uri("/someUri").accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToFlux(SomeDTO.class);
response.subscribe(System.out::println);
Btw, it really doesn’t matter which type I point in accept header, always returning text/html. So how could I get my response converted eventually?
As mentioned in previous answer, you can use exchangeStrategies method,
example:
Flux<SomeDTO> response = WebClient.builder()
.baseUrl(url)
.exchangeStrategies(ExchangeStrategies.builder().codecs(this::acceptedCodecs).build())
.build()
.get()
.uri(builder.toUriString(), 1L)
.retrieve()
.bodyToFlux( // .. business logic
private void acceptedCodecs(ClientCodecConfigurer clientCodecConfigurer) {
clientCodecConfigurer.customCodecs().encoder(new Jackson2JsonEncoder(new ObjectMapper(), TEXT_HTML));
clientCodecConfigurer.customCodecs().decoder(new Jackson2JsonDecoder(new ObjectMapper(), TEXT_HTML));
}
If you need to set the maxInMemorySize along with text/html response use:
WebClient invoicesWebClient() {
return WebClient.builder()
.exchangeStrategies(ExchangeStrategies.builder().codecs(this::acceptedCodecs).build())
.build();
}
private void acceptedCodecs(ClientCodecConfigurer clientCodecConfigurer) {
clientCodecConfigurer.defaultCodecs().maxInMemorySize(BUFFER_SIZE_16MB);
clientCodecConfigurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonDecoder(new ObjectMapper(), TEXT_HTML));
clientCodecConfigurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonEncoder(new ObjectMapper(), TEXT_HTML));
}
Having a service send JSON with a "text/html" Content-Type is rather unusual.
There are two ways to deal with this:
configure the Jackson decoder to decode "text/html" content as well; look into the WebClient.builder().exchangeStrategies(ExchangeStrategies) setup method
change the "Content-Type" response header on the fly
Here's a proposal for the second solution:
WebClient client = WebClient.builder().filter((request, next) -> next.exchange(request)
.map(response -> {
MyClientHttpResponseDecorator decorated = new
MyClientHttpResponseDecorator(response);
return decorated;
})).build();
class MyClientHttpResponseDecorator extends ClientHttpResponseDecorator {
private final HttpHeaders httpHeaders;
public MyClientHttpResponseDecorator(ClientHttpResponse delegate) {
super(delegate);
this.httpHeaders = new HttpHeaders(this.getDelegate().getHeaders());
// mutate the content-type header when necessary
}
#Override
public HttpHeaders getHeaders() {
return this.httpHeaders;
}
}
Note that you should only use that client in that context (for this host).
I'd strongly suggest to try and fix that strange content-type returned by the server, if you can.

Send a Post with url parameter with Retrofit 2

I want to send a POST with Retrofit 2. The url has some parameters:
#Headers({
"Accept: application/x-www-form-urlencoded;",
"User-Agent: my-app"
})
#FormUrlEncoded
#POST("server/directory/location.type")
`public Call<POJOStringValue> dataWithUr(#Path("arg1") String arg1, #Path("arg2"), String arg2);
The url looks like this
www.website.com/server/directory/location.type?arg1=value1&arg2=value2
I was requested to use a POST request. The values (value1 and value2) are dynamic at runtime. I started the project with Xamarin using HttpClient and now I'm rewriting it in Java native. In C# all I had to do was to concact the strings and send the resulting string in a single Post.
I tried to use #Path and the error was :
"server/directory/location.type" does not contain "{arg1}". (parameter #1)
Then, I tried to use #Query and the error was:
java.lang.IllegalArgumentException: Form-encoded method must contain at least one #Field.
Finally I tried with #Field the request never gets any response (I sette the connection timeout to 5 seconds)
Please help me, or tell me if I have to don't have any other choice but to use a GET request.
((EDIT))
Here is my code for the setup of the client:
private static void setupClient(){
final OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
//define retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(iXUtils.getUrl_())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
this.client_ = retrofit.create(RequestInterface.class);
}
The get() method:
public static RequestInterface get(){
return this.client_;
}
Here is how I call it:
public String callFunctionDB(String arg1, String arg2){
setupClient();
Call<POJOStringValue> call = get().dataWithUrlString(arg1, arg2);
try {
POJOStringValue response = call.execute().body();
String value = response.getValue();
int test = 0;
} catch (IOException e) {
String value = "it failded";
e.printStackTrace();
}
return "test";
}
I put the test=0 to be able to put a breaking point, it never gets there. Plus I called the method "callFunctionDB" in a doInbackground to avoid the android.os.NetworkOnMainThreadException.
Retrofit requires you to have at least one form parameter if you request form encoding. You have answered your own question -- you are using query parameters instead of POST fields, so that annotation is not necessary. Remove the #FormUrlEncoded annotation, and change your parameters to #Query annotations.

Redirecting to a page using restful methods?

I've created a page which asks user to fill some form fields and when he submits, the form is sent to a Restful method which you can see below:
#POST
#Path("addUser")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void addUser(#FormParam("username") String username,
#FormParam("password") String password,
#FormParam("id") String id,
#FormParam("group_name") String groupName,
#FormParam("authority_name") String authorityName,
#FormParam("authority_id") String authorityId
)
{
//Something will be done here
}
How can I redirect the user at the end of this function to (let's say) index.jsp?
change your code like this, the addUser() should return a Response Object
public Response addUser(#FormParam("username") String username,
#FormParam("password") String password,
#FormParam("id") String id,
#FormParam("group_name") String groupName,
#FormParam("authority_name") String authorityName,
#FormParam("authority_id") String authorityId
)
{
//Something will be done here
java.net.URI location = new java.net.URI("../index.jsp?msg=A_User_Added");
return Response.temporaryRedirect(location).build()
}
Create a URI using javax.ws.rs.core.UriBuilder that maps the parameters and other data you want to preserve. Then use Response.temporaryRedirect to return a redirect to the client and pass it the URI you’ve built.
Finally I come to this conclusion that there are no other way than what I did:
So here is my solution:
try {
java.net.URI location = new java.net.URI("../index.jsp?msg=A_User_Added");
throw new WebApplicationException(Response.temporaryRedirect(location).build());
} catch (URISyntaxException e) {
e.printStackTrace();
}
By adding this block to my code, I got what I needed. Hope it helps you as well.
See below the usage of redirecting in web services:
public class LoginWebService {
#POST
#Path("/check")
public Response checkDetails(#FormParam("name") String name,#FormParam("pass") String pass ) throws URISyntaxException {
URI uri = new URI("/login/success");
URI uri2= new URI("http://localhost:9090/NewWebServiceproject/new/login/failure");
if(name.equals("admin") && pass.equals("pass"))
//#Path("http://localhost:8010/NewWebServiceproject/new/login/success");
{
return Response.temporaryRedirect(uri).build();
//Response.seeOther(uri);
//return Response.status(200).entity("user successfully login").build();
}
else
{
return Response.temporaryRedirect(uri2).build();
//Response.seeOther(uri2);
//return Response.status(200).entity("user logon failed").build();
}
}
#POST
#Path("/success")
public Response successpage()
{
return Response.status(200).entity("user successfully login").build();
}
#POST
#Path("/failure")
public Response failurepage()
{
return Response.status(200).entity("user logon failed").build();
}
}
It is not good idea to use the "WebApplicationException" in order to redirect the request. in Jersey (2.4.1) you should be able to redirect the request via the normal servlet way, (request.getServletContext().getRequestDispatcher().forward() or just response.sendRedirect())
The following is how Jersey process the request
org.glassfish.jersey.servlet.ServletContainer.service(HttpServletRequest request, HttpServletResponse response)
requestScope.runInScope
final ContainerResponse response = endpoint.apply(data)
methodHandler.invoke(resource, method, args);
Responder.process(ContainerResponse);
That methodHandler is your REST service class, method is the function in that service class.
The step to redirect page become straitforward
Get the (request, response) through Jersey injection (#Context HttpServletRequest request, #Context HttpServletResponse response) in class field or function parameter
Call request.getServletContext().getRequestDispatcher() to get the dispatcher for "forward"
or use Response.sendRedirect(url)
Once you application is returned (just null), Jersey will try to process the result in the "Responder.process(ContainerResponse)". In this step, it will use response to set status (204 no contents for your null return).
So the key point here is you must finalize/close response object before return from your service function. Otherwise, Jersey may overwrite your output.
Small tips on why "WebApplicationException" can overwrite Jersey repsponse. It is because org.glassfish.jersey.server.ServerRuntime.mapException() will use the "webApplicationException.getResponse()" as the return response result.

Categories