How to print response message body as a String within filter method and tried couple of response methods (getEntityOutputStream() / getEntity() / GetContainerResponseWriter() )
public class Test implements ContainerRequestFilter , ContainerResponseFilter) {
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
response.
}
}
I use the Jersey logging to print out request / response body
in my ResourceConfig
registerInstances(new LoggingFilter(myLogger, true));
LoggingFilter
public class LoggingFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter,
ClientResponseFilter, WriterInterceptor {
...
you can check out how they do it : https://github.com/jersey/jersey/blob/master/core-common/src/main/java/org/glassfish/jersey/filter/LoggingFilter.java
I had the same problem and this is what I ended up doing. Basically I return the JSON representation of my entity if I could cast it, else I create a custom response message.
I'm sure there will be better ways to do it.
private String createLoggingResponse(ContainerResponseContext resp) {
Object entity = resp.getEntity();
if (BaseModel.class.isInstance(entity)) {
BaseModel model = (BaseModel) entity;
ByteArrayOutputStream out = new ByteArrayOutputStream();
model.toJson(out);
return new String(out.toByteArray());
} else {
MediaType mediaType = resp.getMediaType();
return "[" + entity.toString() + "," + resp.getStatusInfo().getStatusCode() + "," + resp.getStatusInfo().getReasonPhrase() + "," + mediaType.toString() + "]";
}
}
Related
A SOAP Web-service, which accepts request in following format -
<?xml version = "1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
xmlns:ns="http://...." xmlns:ns1="http://...." xmlns:ns2="http://...."
xmlns:ns3="http://....">
<SOAP-ENV:Header>
<ns:EMContext>
<messageId>1</messageId>
<refToMessageId>ABC123</refToMessageId>
<session>
<sessionId>3</sessionId>
<sessionSequenceNumber>2021-02-24T00:00:00.000+5:00</sessionSequenceNumber>
</session>
<invokerRef>CRS</invokerRef>
</ns:EMContext>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:getEmployee>
<ns:empId>111</ns:empId>
</ns1:getEmployee>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
When trying to make a SOAP request to it using JAXB2, it is giving org.springframework.ws.soap.client.SoapFaultClientException: EMContext Header is missing
I am using
pring-boot-starter
spring-boot-starter-web-services
org.jvnet.jaxb2.maven2 : maven-jaxb2-plugin : 0.14.0
and
Client -
public class MyClient extends WebServiceGatewaySupport {
public GetEmployeeResponse getEmployee(String url, Object request){
GetEmployeeResponse res = (GetEmployeeResponse) getWebServiceTemplate().marshalSendAndReceive(url, request);
return res;
}
}
Configuration -
#Configuration
public class EmpConfig {
#Bean
public Jaxb2Marshaller marshaller(){
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.crsardar.java.soap.client.request");
return jaxb2Marshaller;
}
#Bean
public MyClient getClient(Jaxb2Marshaller jaxb2Marshaller){
MyClient myClient = new MyClient();
myClient.setDefaultUri("http://localhost:8080/ws");
myClient.setMarshaller(jaxb2Marshaller);
myClient.setUnmarshaller(jaxb2Marshaller);
return myClient;
}
}
App -
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Bean
CommandLineRunner lookup(MyClient myClient){
return args -> {
GetEmployeeRequest getEmployeeRequest = new GetEmployeeRequest();
getEmployeeRequest.setId(1);
GetEmployeeResponse employee = myClient.getEmployee("http://localhost:8080/ws", getEmployeeRequest);
System.out.println("Response = " + employee.getEmployeeDetails().getName());
};
}
}
How can I add EMContext Header to the SOAP request?
The server is complaining because your Web Service client is not sending the EMContext SOAP header in your SOAP message.
Unfortunately, currently Spring Web Services lack of support for including SOAP headers in a similar way as the SOAP body information is processed using JAXB, for example.
As a workaround, you can use WebServiceMessageCallback. From the docs:
To accommodate the setting of SOAP headers and other settings on the message, the WebServiceMessageCallback interface gives you access to the message after it has been created, but before it is sent.
In your case, you can use something like:
public class MyClient extends WebServiceGatewaySupport {
public GetEmployeeResponse getEmployee(String url, Object request){
// Obtain the required information
String messageId = "1";
String refToMessageId = "ABC123";
String sessionId = "3";
String sessionSequenceNumber = "2021-02-24T00:00:00.000+5:00";
String invokerRef = "CRS";
GetEmployeeResponse res = (GetEmployeeResponse) this.getWebServiceTemplate().marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
// Include the SOAP header content for EMContext
try {
SoapMessage soapMessage = (SoapMessage)message;
SoapHeader header = soapMessage.getSoapHeader();
StringSource headerSource = new StringSource(
"<EMContext xmlns:ns=\"http://....\">" +
"<messageId>" + messageId + "</messageId>" +
"<refToMessageId>" + refToMessageId + "</refToMessageId>" +
"<session>" +
"<sessionId>" + sessionId + "</sessionId>" +
"<sessionSequenceNumber>" + sessionSequenceNumber + "</sessionSequenceNumber>" +
"</session>" +
"<invokerRef>" + invokerRef + "</invokerRef>" +
"</EMContext>"
);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
} catch (Exception e) {
// handle the exception as appropriate
e.printStackTrace();
}
}
});
return res;
}
}
Similar questions have been posted in SO. Consider for instance review this or this other.
I calling to the api with the basic retrofit Call object:
public interface dataApi {
#GET("animal/cats")
Call<AllAnimals> getAllData(
#Query("api_key") String apiKey
);
}
And I can get the response inside my view model like this:
call.enqueue(new Callback<AllAnimals>() {
#Override
public void onResponse(Call<AllAnimals> call, Response<AllAnimals> response) {
animals.setValue(response.body());
}
#Override
public void onFailure(Call<AllAnimals> call, Throwable t) {
Log.i(TAG, "onFailure: " + t);
}
});
Nothing speical here.
I've several problem with this approach
FIRST - if I give the wrong api key for example, the response should give me a response with the code of the problem, instead I just get null body.
SECOND I am planning to have more api calls, and it's a huge code duplication to handle errors every call I wrote.
How can I implement custom error handling for this situation, that will be apply to other calls too?
I think you can use okhttp interceptor and define yourself ResponseBody converter to fix your problem.
First,intercept you interested request and response;
Second,check the response,if response is failed then modify the response body to empty。
define a simple interceptor
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
System.out.println(request.url());
okhttp3.Response response = chain.proceed(request);
if (!response.isSuccessful() && url.contains("animal/cats")) {
// request failed begin to modify response body
response = response.newBuilder()
.body(ResponseBody.create(MediaType.parse("application/json"), new byte[] {}))
.build();
}
return response;
}
};
define self ResponseBody converter
most code from com.squareup.retrofit2:converter-jackson we just add two lines:
final class JacksonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final ObjectReader adapter;
JacksonResponseBodyConverter(ObjectReader adapter) {
this.adapter = adapter;
}
#Override public T convert(ResponseBody value) throws IOException {
try {
if (value.contentLength() == 0) {
return null;
}
return adapter.readValue(value.charStream());
} finally {
value.close();
}
}
}
the below code is added:
if (value.contentLength() == 0) {
return null;
}
I've created a rest api using Jersey/Jackson and it works well. I want to adjust my POST methods to receive a string token in addition to the POJO they are receiving as JSON. I've adjusted one of my methods like so:
#POST
#Path("/user")
#Consumes(MediaType.APPLICATION_JSON)
public Response createObject(User o, String token) {
System.out.println("token: " + token);
String password = Tools.encryptPassword(o.getPassword());
o.setPassword(password);
String response = DAL.upsert(o);
return Response.status(201).entity(response).build();
}
I want to call that method, but for whatever reason token prints to null no matter what I try. Here is the client code I've written to send the post request:
public String update() {
try {
com.sun.jersey.api.client.Client daclient = com.sun.jersey.api.client.Client
.create();
WebResource webResource = daclient
.resource("http://localhost:8080/PhizzleAPI/rest/post/user");
User c = new User(id, client, permission, reseller, type, username,
password, name, email, active, createddate,
lastmodifieddate, token, tokentimestamp);
JSONObject j = new JSONObject(c);
ObjectMapper mapper = new ObjectMapper();
String request = mapper.writeValueAsString(c) + "&{''token'':,''"
+ "dog" + "''}";
System.out.println("request:" + request);
ClientResponse response = webResource.type("application/json")
.post(ClientResponse.class, request);
if (response.getStatus() != 201) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}
System.out.println("Output from Server .... \n");
String output = response.getEntity(String.class);
setId(UUID.fromString(output));
System.out.println("output:" + output);
return "" + output;
} catch (UniformInterfaceException e) {
return "failue: " + e.getMessage();
} catch (ClientHandlerException e) {
return "failue: " + e.getMessage();
} catch (Exception e) {
return "failure: " + e.getMessage();
}
}
Any help would be greatly appreciated.
This is not the way JAX-RS works. The body of your POST request will get marshaled to the first argument of your annotated resource method (in this case, into the User argument). You have a couple options to get around this:
Create a wrapper object containing both a User object and token. Send that back and forth between your client and server.
Specify the token as a query parameter on your URL and access it on the server side as a #QueryParam.
Add the token as a header parameter and access it on the server side as a #HeaderParam.
Example - Option 1
class UserTokenContainer implements Serializable {
private User user;
private String token;
// Constructors, getters/setters
}
Example - Option 2
Client:
WebResource webResource = client.
resource("http://localhost:8080/PhizzleAPI/rest/post/user?token=mytoken");
Server:
#POST
Path("/user")
#Consumes(MediaType.APPLICATION_JSON)
public Response createObject(#QueryParam("token") String token, User o) {
System.out.println("token: " + token);
// ...
}
Example - Option 3
Client:
ClientResponse response = webResource
.type("application/json")
.header("Token", token)
.post(ClientResponse.class, request);
Server:
#POST
Path("/user")
#Consumes(MediaType.APPLICATION_JSON)
public Response createObject(#HeaderParam("token") String token, User o) {
System.out.println("token: " + token);
// ...
}
In case you're using Jersey 1.x, best approach is to post multiple objects as #FormParam
At least two advantages:
You don't need to use a wrapper object to post multiple parameters
The parameters are sent within the body rather than in the url (as with #QueryParam and #PathParam)
Check this example:
Client: (pure Java):
public Response testPost(String param1, String param2) {
// Build the request string in this format:
// String request = "param1=1¶m2=2";
String request = "param1=" + param1+ "¶m2=" + param2;
WebClient client = WebClient.create(...);
return client.path(CONTROLLER_BASE_URI + "/test")
.post(request);
}
Server:
#Path("/test")
#POST
#Produces(MediaType.APPLICATION_JSON)
public void test(#FormParam("param1") String param1, #FormParam("param2") String param2) {
...
}
I'm currently using Jersey as a proxy REST api to call another RESTful web service. Some of the calls will be passed to and from with minimal processing in my server.
Is there a way to do this cleanly? I was thinking of using the Jersey Client to make the REST call, then converting the ClientResponse into a Response. Is this possible or is there a better way to do this?
Some example code:
#GET
#Path("/groups/{ownerID}")
#Produces("application/xml")
public String getDomainGroups(#PathParam("ownerID") String ownerID) {
WebResource r = client.resource(URL_BASE + "/" + URL_GET_GROUPS + "/" + ownerID);
String resp = r.get(String.class);
return resp;
}
This works if the response is always a success, but if there's a 404 on the other server, I'd have to check the response code. In other words, is there clean way to just return the response I got?
There is no convenience method as far as I am aware. You can do this:
public Response getDomainGroups(#PathParam("ownerID") String ownerID) {
WebResource r = client.resource(URL_BASE + "/" + URL_GET_GROUPS + "/" + ownerID);
ClientResponse resp = r.get(ClientResponse.class);
return clientResponseToResponse(resp);
}
public static Response clientResponseToResponse(ClientResponse r) {
// copy the status code
ResponseBuilder rb = Response.status(r.getStatus());
// copy all the headers
for (Entry<String, List<String>> entry : r.getHeaders().entrySet()) {
for (String value : entry.getValue()) {
rb.header(entry.getKey(), value);
}
}
// copy the entity
rb.entity(r.getEntityInputStream());
// return the response
return rb.build();
}
for me answer from Martin throw:
JsonMappingException: No serializer found for class sun.net.www.protocol.http.HttpURLConnection$HttpInputStream
Change from
rb.entity(r.getEntityInputStream());
to
rb.entity(r.getEntity(new GenericType<String>(){}));
helped.
Okay, I have a class NamedSystems, that has as its only field a Set of NamedSystem.
I have a method to find NamedSystems by certain criteria. That's not really important. When it gets results, everything works fine. However, when it can't find anything, and thus returns a null (or empty -- I've tried both ways) set, I get problems. Let me explain.
I'm using the Spring RestTemplate class and I'm making a call like this in a unit test:
ResponseEntity<?> responseEntity = template.exchange(BASE_SERVICE_URL + "?
alias={aliasValue}&aliasAuthority={aliasAssigningAuthority}",
HttpMethod.GET, makeHttpEntity("xml"), NamedSystems.class,
alias1.getAlias(), alias1.getAuthority());
Now, since this would normally return a 200, but I want to return a 204, I have an interceptor in my service that determines if a ModelAndView is a NamedSystem and if its set is null. If so, I then the set the status code to NO_CONTENT (204).
When I run my junit test, I get this error:
org.springframework.web.client.RestClientException: Cannot extract response: no Content-Type found
Setting the status to NO_CONTENT seems to wipe the content-type field (which does make sense when I think about it). So why is it even looking at it?
Spring's HttpMessageConverterExtractor extractData method:
public T extractData(ClientHttpResponse response) throws IOException {
MediaType contentType = response.getHeaders().getContentType();
if (contentType == null) {
throw new RestClientException("Cannot extract response: no Content-Type found");
}
for (HttpMessageConverter messageConverter : messageConverters) {
if (messageConverter.canRead(responseType, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + responseType.getName() + "] as \"" + contentType
+"\" using [" + messageConverter + "]");
}
return (T) messageConverter.read(this.responseType, response);
}
}
throw new RestClientException(
"Could not extract response: no suitable HttpMessageConverter found for response type [" +
this.responseType.getName() + "] and content type [" + contentType + "]");
}
Going up the chain a bit to find out where that Extractor is set, I come to RestTemplate's exchange() method that I used in the test:
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(requestEntity, responseType);
ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
So, it's trying to convert what amounts to nothing because of the supplied response type from the exchange call. If I change the responseType from NamedSystems.class to null, it works as expected. It doesn't try to convert anything. If I had tried to set the status code to 404, it also executes fine.
Am I misguided, or does this seem like a flaw in RestTemplate? Sure, I'm using a junit right now so I know what's going to happen, but if someone is using RestTemplate to call this and doesn't know the outcome of the service call, they would naturally have NamedSystems as a response type. However, if they tried a criteria search that came up with no elements, they'd have this nasty error.
Is there a way around this without overriding any RestTemplate stuff? Am I viewing this situation incorrectly? Please help as I'm a bit baffled.
One more way to solve this would be to make response entity as null as shown below.
ResponseEntity<?> response = restTemplate.exchange("http://localhost:8080/myapp/user/{userID}",
HttpMethod.DELETE,
requestEntity,
null,
userID);
If you still need response headers, try implementing the ResponseErrorHandler.
I believe you should probably look at the ResponseExtractor interface & call execute on the RestTemplate providing your implementation of the extractor. To me it looks like a common requirement to do this so have logged this:
https://jira.springsource.org/browse/SPR-8016
Here's one I prepared earlier:
private class MyResponseExtractor extends HttpMessageConverterExtractor<MyEntity> {
public MyResponseExtractor (Class<MyEntity> responseType,
List<HttpMessageConverter<?>> messageConverters) {
super(responseType, messageConverters);
}
#Override
public MyEntity extractData(ClientHttpResponse response) throws IOException {
MyEntity result;
if (response.getStatusCode() == HttpStatus.OK) {
result = super.extractData(response);
} else {
result = null;
}
return result;
}
}
I've tested this & it seems to do what I want.
To create the instance of the ResponseExtractor I call the constructor & pass the converters from a RestTemplate instance that's been injected;
E.g.
ResponseExtractor<MyEntity> responseExtractor =
new MyResponseExtractor(MyEntity.class, restTemplate.getMessageConverters());
Then the call is:
MyEntity responseAsEntity =
restTemplate.execute(urlToCall, HttpMethod.GET, null, responseExtractor);
Your mileage may vary. ;-)
Here's a simple solution where you can set the default Content-Type for use if it is missing in the response. The Content-Type is added to the response header before it is handed back off to the preconfigured ResponseExtractor for extraction.
public class CustomRestTemplate extends RestTemplate {
private MediaType defaultResponseContentType;
public CustomRestTemplate() {
super();
}
public CustomRestTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
public void setDefaultResponseContentType(String defaultResponseContentType) {
this.defaultResponseContentType = MediaType.parseMediaType(defaultResponseContentType);
}
#Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException {
return super.doExecute(url, method, requestCallback, new ResponseExtractor<T>() {
public T extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().getContentType() == null && defaultResponseContentType != null) {
response.getHeaders().setContentType(defaultResponseContentType);
}
return responseExtractor.extractData(response);
}
});
}
}
This should now be fixed in Spring 3.1 RC1.
https://jira.spring.io/browse/SPR-7911
Or you could extend RestTemplate and override doExecute(..) and check the response body.
For example here is what I implemented and works for us:
#Override
protected <T> T doExecute(final URI url, final HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException
{
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try
{
final ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null)
{
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response))
{
logResponseStatus(method, url, response);
}
else
{
handleResponseError(method, url, response);
}
if ((response.getBody() == null) || (responseExtractor == null))
{
return null;
}
return responseExtractor.extractData(response);
}
catch (final IOException ex)
{
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
}
finally
{
if (response != null)
{
response.close();
}
}
}
I think you are right.
I'm having a similar problem.
I think we should be getting a ResponseEntity with a HttpStatus of NO_CONTENT and a null body.
I came along a workaround (not sure if it meets your case):
First define a custom interceptor class which implements ClientHttpRequestInterceptor. and check if response.getStatusCode() meets your case (my case is != HttpStatus.NOT_FOUND and response.getBody() length is 0), define a custom class (e.x. DefaultResponseForEmptyRestTemplateBody) which has a static method of type MockClientHttpResponse:
public class RequestResponseInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
if(response.getStatusCode()!=HttpStatus.NOT_FOUND && response.getBody().readAllBytes().length==0){
response = DefaultResponseForEmptyRestTemplateBody.getResponse(response.getStatusCode());
}
return response;
}
}
public static class DefaultResponseForEmptyRestTemplateBody {
MockClientHttpResponse response;
private static byte[] content = new byte[0];
public static MockClientHttpResponse getResponse(HttpStatus statusCode){
content = "response body is empty".getBytes();
return new MockClientHttpResponse(content, statusCode);
}
}
finally add this interceptor to your restTemplate object as below:
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
and call your restTemplate.postForEntity:
ResponseEntity<String> response = this.restTemplate.postForEntity(baseUrl, requestParams,String.class);