I am calling a SOAP webservice in a Spring Boot project using Spring WebserviceTemplate. My client class extends WebServiceGatewaySupport. I cannot post the actual code, but this is a sample.
Response response = (Response) getWebServiceTemplate().marshalSendAndReceive(request, new SoapActionCallback("http://tempuri.org/rpc/Gateway/"));
When this request is sent, I am getting response code 400. I enabled trace logging for Spring and found that an empty request is sent.
TRACE o.s.ws.client.MessageTracing.sent.sendRequest - Sent request []
When I tried to create a simpler web service client using the same components - Spring Boot project using Spring WebserviceTemplate, I can see that it sends a valid XML payload and it works.
I have now found that this error happens only as part of a larger existing project - the request object being marshaled as an empty string. It is just sending an empty payload. There are no errors in the log before the call is made to webservice.
The request and response POJO are generated by maven-jaxb2-plugin & I have verified that the package and schema are correct.
I have verified that the Marshaller (Jaxb2Marshaller) and its context path are set correctly, and it marshals the request object when called separately.
How can I debug this? Please give your suggestions.
Related
I have a webservice which calls another WS and returns the response from the second WS. It looks like so:
// MyController
public ResponseEntity<Foo> requestFooController(#RequestBody #Valid Bar request) {
return this.myService.requestFooService(request);
}
//MyService
ResponseEntity<Foo> requestFooService(Bar request) {
Buzz improvedRequest = ...
return this.secondWS.secondRequestFoo(improvedRequest);
}
When I call the API through Postman, I receive a HTTP OK response with an empty body. Yet, when I'm in debug mode I can see that the service is returning a ResponseEntity with a body. The headers are not lost though.
I changed my code like so and it works fine:
// MyController
public ResponseEntity<Foo> requestFooController(#RequestBody #Valid Bar request) {
ResponseEntity<Foo> tmp = this.myService.requestFooService(request);
return ResponseEntity.status(tmp.getStatusCode()).body(tmp.getBody());
}
Now through Postman I do have the expected body. However, I don't understand the behaviour. I thought that maybe it's due to the fact that the body is some kind of stream that can be read once or something similar. But from reading the source code I don't see anything that could explain this behaviour.
I'm using the Netflix-stack (so HTTP calls between the two WS are made through a Feign client).
Any idea why I'm getting this result?
EDIT:
More details on my stask:
SpringBoot 1.5.3.RELEASE
Feign 2.0.5
There is a bug that causes the named body of an HTTP MultiPart POST to fail. The symptom of this is that you make a POST request with a body, and Spring-Boot can't match it up to an endoint. The exception I see is:
2019-01-23 15:22:45.046 DEBUG 1639 --- [io-8080-exec-10] .w.s.m.m.a.ServletInvocableHandlerMethod : Failed to resolve argument 3 of type 'org.springframework.web.multipart.MultipartFile'
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
Zuul is doing caching of the request in order to re-try multiple times. In this process, it fails to preserve the named field for the binary body. You may find it working if you preface the request with zuul. So instead of http://myserver.com/myservice/endpoint use zuul in the path: http://myserver.com/zuul/myservice/endpoint
That will effectively avoid the saving of the request and the retry mechanism.
More details are available on this issue in Zuul's GitHub Bug List.
I would like to provide the actual get request in a response. I am free to choose between placing that information into the header or the body of the response.
The current project setup includes:
Nginx as reverse proxy
Spring boot application
which are potential candidates to put in that logic.
Request:
GET http://localhost/api/users/2/photos/2
Response:
BODY {"some": "values",
"_request": "GET http://localhost/api/users/2/photos/2"}
or
HEADERS "custom-header-get-request": "GET http://localhost/api/users/2/photos/2"
BODY {"some": "values"}
I can easily add the information to the response of each Responseobject within the Controllers of the spring boot application. This approach is quite cumbersome, as it leads to a lot of codechanges and allways needs to be in mind that each Controller should take care of providing the requestinformation to the response, achieving the first solution.
Which other options do i have?
I have a JAX-RS client that's making a simple GET request. I'm using the CXF implementation and Spring for DI. The call is successfull and I get a response code of 200 back. But I'm getting an error when reading the response into my POJO.
Exception:
[2015-05-08 16:11:55,457][ERROR][org.apache.cxf.jaxrs.utils.JAXRSUtils]: No message body reader has been found for class com.voya.refapp.domain.Customer, ContentType: application/json
[2015-05-08 16:11:55,468][ERROR][com.voya.refapp.service.CustomerServiceImpl]: filterByName() - Exception occurred
javax.ws.rs.client.ResponseProcessingException: No message body reader has been found for class com.voya.refapp.domain.Customer, ContentType: application/json
at org.apache.cxf.jaxrs.impl.ResponseImpl.reportMessageHandlerProblem(ResponseImpl.java:433) ~[cxf-rt-frontend-jaxrs-3.0.4.jar:3.0.4]
at org.apache.cxf.jaxrs.impl.ResponseImpl.doReadEntity(ResponseImpl.java:384) ~[cxf-rt-frontend-jaxrs-3.0.4.jar:3.0.4]
Code:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/rest").path("customers/1");
Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON_TYPE);
Response response = builder.get(); // Successful
Customer customer = response.readEntity(Customer.class); // Fails
I have the below dependency as suggested in this answer in my classpath, it doesn't seem to be picked up automatically.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
I also tried registering the json provider when creating the client:
Client client = ClientBuilder.newClient().register(new JacsksonJsonProvider());
and
Client client = ClientBuilder.newClient().register(JacsksonJsonProvider.class);
But none of these options worked too. I got a different exception when I registered the json provider using one of the options above:
javax.ws.rs.client.ResponseProcessingException: Problem with reading the data
Update:
Registering the json provider worked fine using ClientBuilder.newClient().register(JacsksonJsonProvider.class). The issue was with the data (like the exception above clearly states.. I feel silly now :(). I had boolean field in the json named "active", but it was called "isActive" in the POJO. Once I added the annotation #JsonProperty("active") to the field in POJO, it started working fine
AFAIK CXF does not support autodiscovery of MessageBodyReader classes. But registering manually JacksonJsonProvider should work for you.
Please check my example that works perfectly well. It is almost exactly the same as yours, I just used different service. Maybe you can spot a difference that prevents your version from working correctly.
Client client = ClientBuilder.newClient().register(JacksonJsonProvider.class);
WebTarget target = client.target("http://jsonplaceholder.typicode.com").path("posts/1");
Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON_TYPE);
Response response = builder.get(); // Successful
Post post = response.readEntity(Post.class);
My first question on this community that has helped me so much already.
I'm using RestEasy and trying to do a POST request to a REST service sending a JSON object. The problem is that my JSON object keeps going as a request parameter and not in the request body, which is what I need.
Here is how I'm doing it.
Invocation inv = target.request().buildPost(Entity.json(shipment));
Response response = inv.invoke();
I've been looking for ours on how to put the JSON object into the request body but found nothing.
Any ideas?
This is resolved. There was a problem on the web service that was receiving my request.
Thanks everyone for your comments!**
I have written a custom interceptor that does some parameter validation. I want to be able to return an error code and serialize a JAXB-annotated class as the response body.
If I throw a WebApplicationException, it doesn't have any special processing done to serialize the Response object inside (which makes sense; I assume that is done by another interceptor).
How should I go about stopping the interceptor chain but still have JAXB serialize the response entity?
Well, at least in the CXF JAX-RS interceptor flow, if you set:
message.getExchange().put(Response.class, response);
...then the actual service does not get invoked, while the other phases do get invoked. Haven't dug in to the CXF code to see where that logic kicks in.
So I built a response like this:
Response response = Response
.status(Response.Status.FORBIDDEN)
.entity(new ErrorEntity("This is a JAXB object with an error string"))
.build();
I also have some custom authentication running in a CXF JAX-RS filter and I only want to check the parameters when the authentication is alright, so I set my parameter interceptor class to run during the PRE_INVOKE phase.