rest-assured body(InputStream) does not work - java

I am using rest-assured 4.3.3 (the version is forced by the newest Spring Boot 2.5.0).
Rest-assured interprets the InputStream as Object and cannot handle it. When I press Ctrl+Q in IntelliJ IDEA (Javadoc) over .body(getInputStream()), it shows the signature RequestSpecification body(InputStream body) so the compiler should understand properly which of the the heavily overloaded .body() methods is supposed to be used.
However, when I replace getInputStream() with getFile(), it works. That's how I know that the XML in the resource file is correct.
The documentation says for both the methods body(File body) as well as body(InputStream body) exactly the same:
Specify file content that'll be sent with the request.
So my understanding is that these are just two alternative ways how to pass the body if I have it in a file.
My code:
class MyTest {
#Value("classpath:/valid-request.xml")
private Resource validRequest;
....
#Test
void myTest() {
given()
.header("Content-Type", "text/xml; charset=UTF-8")
.header("SOAPAction", "myService")
.body(validRequest.getInputStream()) // this does not work
// .body(validRequest.getFile()) // this works
.when()
.post("/myApi")
.then()
.log().ifValidationFails()
.statusCode(200)
.contentType("text/xml")
;
}
}
Error message:
java.io.BufferedInputStream#6ceb953
2021-06-11 17:35:47.083 ERROR [,628aa0b986fe0c98,628aa0b986fe0c98] 11428 --- [o-auto-1-exec-1] com.sun.xml.messaging.saaj.soap : SAAJ0511: Unable to create envelope from given source
I think that it is caused by a bug in rest-assured, but just for a check, maybe you might spot a flaw in my code better than I.
Anyway, I created an issue https://github.com/rest-assured/rest-assured/issues/1480.
The same error has been reported as "fixed" in the closed issue https://github.com/rest-assured/rest-assured/issues/1040.

Actually no, it's not a bug.
It says InputStream as a method parameter, because it expects that you will pass an object, which will be interpreted and serialized by InputStream.
The best way is to use POJO-classes for request body payloads, however in your case I'd suggest to go ahead and read that file as a String and pass as a param into body(), this should be working.

Related

Is it possible to include a request body in a GET request using Spring WebClient?

I know sending a body with a GET request isn't the best idea but I'm trying to consume an existing API which requires it.
Sending a body with POST is straight-forward:
webClient.post()
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
It won't work with webClient.get() though, because while the post() method returns a WebClient.RequestBodyUriSpec, the get() method returns WebClient.RequestHeadersUriSpec<?>, which doesn't seem to allow any body definitions.
I've found a workaround for Spring RestTemplate here: RestTemplate get with body,
but had no luck finding any for the new WebClient.
While the other responses are correct that you shouldn't use a body with a GET request, that is not helpful when you do not own, or cannot change the already existing method you are calling.
The problems is WebClient#get returns a WebClient.RequestHeadersUriSpec which does not provide a way for us to set the body.
WebClient#post returns a WebClient.RequestBodyUriSpec which does provide us a way to set the body but will cause us to use the wrong HTTP method, POST instead of GET.
Thankfully for us stuck in this situation there is WebClient#method which returns a WebClient.RequestBodyUriSpec and allows us to set the HTTP method.
webClient.method(HttpMethod.GET)
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
You may still run into issues in your testing libraries though...
A GET reques has no body. It is forbidden (well, not forbidden, but not used at all) by the HTTP specification. You have two approaches here:
Do a POST. It is there just for that.
Use a query string and pass the data in that part of the URL.
Of course, you can attach the needed fields and pass a payload to the GET request, but it will probably be ignored, or worse, identified as an error and rejected by the server, before your served code has access to it. But if you are passing data to the server to do some processing with it, then POST is what you need to use.
Extracted from RFC-7231. HTTP 1.1. Semantics and code:
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
(markup is mine)
Reasons for this are, mainly, that a GET method must be idempotent, producing the same output for the same URL, if repeated. POST doesn't have these requirements, so POST is your friend.

Problems with WebClient returning empty response

I am working on a project which until now has been using the org.springframework.web.client.RestTemplate
I have recently understood that this class is to be deprecated in favour of the Asynch org.springframework.web.reactive.function.client.WebClient framework.
Now I am massively in favour of this, as my application is suffering from long delays on waiting from
responses from RestTemplate (GET) calls (in which time I could be doing database stuff etc.).
The problem that I have now is that if I make a call like:
final Mono<String> call = webClient
.get()
.uri("/base/recordPath/1?format=json")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
when I make a subsequent call like:
System.out.println(call.block());
I get the expected output (a String version of a populated Json Object).
however if I change the earlier call to (which I want to do!):
final Mono<JsonObject> call = webClient
.get()
.uri("/base/recordPath/1?format=json")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JsonObject.class)
when I do:
System.out.println(call.block());
I just get {} instead of a populated JsonObject
So it looks like the bodyToMono() hasn't done what 'I' expected
When I used RestTemplate, there was a way to register serialisers with the Template (though this wasn't necessary for JsonObject),
is this also necessary with WebClient? If so how do you do it?
I would be grateful of help.
Thanks
Bill
N.B. I'm not sure if this has any relevance, but the rest endpoint I am accessing does have an IP restriction, so if in some way the WebClient were to alter the originating IP, this may have some effect. Though I would have thought it would be more like a 4** of some sort, I'm not seeing any of those!
Or possibly a Media type issue, as it is going through a DMZ, where the guardian may be changing an 'unauthorised' request from application/json to text/* for example.
Another point which may have relevance is that to get a successful start of the application it is necessary to run it with the following property:
spring.main.web-application-type=none
Update
I now have my application running, though not as I want!
The issue appeared to be transitive dependencies imported by the team pom I am required to use (as a parent pom).
I now get a successful start of the project. But still find that the json Object (which is now Jackson) is still Empty (as reported by object.isEmpty()).
my dependencies/versions now are:
org.springframework:5.2.8.RELEASE
org.springframework.boot:2.3.3.RELEASE
com.fasterxml.jackson:2.11.2
I know I am fighting a parent pom which is against what am trying to do but would like to know what the dependencies I really need are

Spring Boot - postForEntity(): what is content type of the response?

I am attempting to set up and run integration tests on a set of Spring Boot microservices that communicate via HTTP REST. I am using the Citrus Framework for the integration test framework.
I have a test scenario that involves one "master" service calling two other services to do work. My test has the calls to start the process and "mock" the worker services. I'll include source below.
I'm running into an issue where I get an exception that seems to indicate that a message the test is expecting to receive (as application/json) is coming through as text/plain and it cannot find a message converter to use. The odd thing is that the message that's is being received should be JSON (or least look like JSON).
I encountered a similar issue on the sending end (the POSTer), where Citrus was having a problem with receiving a message. I traced it down to the fact that I had not been setting any HTTP headers, specifically Accept and Content-Type. Once I set these appropriately, Citrus was happy with what it received.
The service code:
HttpEntity<GenerateRouteCommand> entity =
(HttpEntity<GenerateRouteCommand>) HttpEntityBuilder.createHttpEntity(...);
ResponseEntity<GenerateRouteStatus> response =
rgTemplate.postForEntity(url, entity, GenerateRouteStatus.class);
The exception:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class edu.mit.ll.mission_services.messages.GenerateRouteStatus] and content type [text/plain;charset=utf-8]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:121)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:994)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:977)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:737)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:445)
at edu.mit.ll.mission_services.service.mission_planner.service.MissionPlanner.postGenerateRoute(MissionPlanner.java:214)
at edu.mit.ll.mission_services.service.mission_planner.service.MissionPlanner.planMission(MissionPlanner.java:144)
at edu.mit.ll.mission_services.service.mission_planner.controller.MissionPlannerController$Runner.executeTask(MissionPlannerController.java:51)
at edu.mit.ll.mission_services.common.util.ITask.run(ITask.java:37)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
The entity that's being sent in the POST request has the Accept and Content-Type headers set to application/json. I think I'm OK there. The stack trace above seems to be saying that the response message either has no headers set (or set to the wrong values) and since the test case expects JSON, fails the test.
When these services are run "normally" (i.e. not being driven via Citrus), everything works fine. No problems that I've been able to discern.
I've recently ran into this issue with spring-boot twice while trying to decode REST Respones. I solved this issue by explicitly setting HttpMessageConverters on the RestTemplate.
If I were you I would debug and see what the content of the response actually looks like. If it is JSON try using GsonHttpMessageConverter or if it really is just text, try StringHttpMessageConverter. Alternatively add both to the RestTemplate via the following:
private final RestTemplate restTemplate = new RestTemplateBuilder()
.messageConverters(Lists.newArrayList(new GsonHttpMessageConverter(GsonHelper.getInstance()), new StringHttpMessageConverter()))
.build();
If neither of these converters appear to solve your exception, take a look at all the different implementations of the HttpMessageConverter interface and deduce which one would suite your specific case. There are a fair number of implementations implementing this interface.
Figured out what I needed to do to set the expected content type of application/json for a message returned from a mocked server in Citrus. I was missing the following statement:
.contentType(ContentType.APPLICATION_JSON.getMimeType())
Some context:
// Set route assessor to return response.
runner.http(builder -> builder
.server(raServer)
.send()
.response(HttpStatus.OK)
.messageType(MessageType.JSON)
.contentType(ContentType.APPLICATION_JSON.getMimeType())
.payload(new ClassPathResource("templates/assess-route-status.json")));
Evidently, it defaults to text/plain if not specified.

Spring HttpClientErrorException provides no details from response body

I'm updating legacy code that uses the exchange method of Spring 3.1 Framework's RestTemplate class. I came across what appears to be a major omission of detail. When the rest client with which I am attempting to communicate returns a 400 status code, a HttpClientErrorException is thrown but there is no response body to provide detail as to why the server rejected the request. There looks like there is no way to retrieve the response body which would provide very helpful information.
I do not need to figure out what is wrong in my calling code as I have already done that. I just would like to know if there is some way I could access and log the body of the HTTP response if an error occurs on the call.
The response body is actually a property on HttpClientErrorException. It can be accessed via the following two accessors which it inherits from its parent class HttpStatusCodeException:
public byte[] getResponseBodyAsByteArray()
public String getResponseBodyAsString()
Cast your HttpClientErrorException e to HttpStatusCodeException:
((org.springframework.web.client.HttpStatusCodeException) e).getResponseBodyAsString()

Using wiremock, can I return a body that is dependent on the post request

I am trying to test an openid provider class. The openid consumer class is making an http request. I am mocking the response to this request using wiremock. I am trying to mock a valid openid response. However, the valid response depends on the request parameters. Using wiremock, can I set up a mock request where the body of the response is dependent on the request parameters?
This is possible, you just have to make use of a ResponseTansformer. In the below example code the responseDefinition is determined by the stubbing given below. Here I mock an encoding service by simply returning the body bytes back to the caller. Although in the transformer I am free to return whatever I like based on the contents of the request.
int port = 8080;
WireMockServer wireMockServer = new WireMockServer(new WireMockConfiguration().port(port).extensions(new ResponseTransformer() {
#Override
public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files) {
return new ResponseDefinitionBuilder().like(responseDefinition)
.withBody(request.getBodyAsString().getBytes())
.build();
}
#Override
public String name() {
return "request body returning request transformer";
}
}));
wireMockServer.start();
WireMock.configureFor("localhost", port);
stubFor(post(urlEqualTo("/encode"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/octet-stream")
.withStatus(200)));
stubFor(post(urlEqualTo("/decode"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/octet-stream")
.withStatus(200)));
Wiremock supports extensions that you can write yourself that act as a middleware used to intercept the request and response bodies so you can format it however you like. It's very flexible and allows you to make up new response bodies dynamically or even no response at all.
As an example, we wrote an extension for this at Opentable and open sourced it on Maven Central. It allows you treat the json attributes as variables and interpolate them into your response body. Check it out. Let us know how it goes or if you have any questions.
https://github.com/opentable/wiremock-body-transformer
As far as I know and my experience with WireMock, no.
You can't parameterize a response with arguments passed through request. The best you can do is use matchers to make your mocked server respond accordingly.
I would recommend you making some unit or integration tests with plain jUnit in order to test requests/responses in such cases. They should be quicker if you want to test that receipt requests are responding correctly. I see WireMock as an alternative to do acceptance test, to ensure that your interface with other REST services are not getting broken.
I've never used wiremock. But according to their online documentation you can write a mock that matches URL and Request body parameters. So you should be able to return different mocks depending on the parameters in either the URL itself or embedded in the request body.
Yes it is possible to create a stub with the request matching in wiremock.
Following attributes are supported by for Request matching request.
URL
HTTP Method
Query parameters
Headers
Basic authentication (a special case of header matching)
Cookies
Request body
Multipart/form-data
In your scenario if you want to apply matching on the values in the request body you can use the below approach for generating stub for it.
{
"request": {
...
"bodyPatterns" : [ {
"equalToJson" : "{ \"total_results\": 4 }"
} ]
...
},
...
}
Follow the link for more details: http://wiremock.org/docs/request-matching/

Categories

Resources