I'm currently trying to use wiremock to mock the result of http calls in my unit tests, and when I try to get the response body, I got some encoding issues.
I write one methode to stub a post methode
public static void setupMockExecutionResponse(WireMockServer mockService) throws IOException {
mockService.stubFor(WireMock.post(WireMock.urlEqualTo("/reportExecutions"))
.willReturn(WireMock.aResponse()
.withStatus(HttpStatus.OK.value())
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE+"; charset=utf-8")
.withHeader("set-cookie", "JSESSIONID=1111111111111; SERVERID=jasper")
.withBody(
copyToString(
JasperClientMock.class.getClassLoader().getResourceAsStream("payload/execution-response.json"),
StandardCharsets.UTF_8))));
}
As you can see I specified that the charset of the response body is UTF-8, and in the header I add it too.
The json file used as the response is also encoded in UTF-8
{
"status":"ready",
"totalPages":1,
"requestId":"217f7dc9-47c4-4c44-bada-7e29b653887b",
"reportURI":"/test/test/test/test/export_test",
"exports":[
{
"status":"ready",
"outputResource":{
"contentType":"application/pdf",
"fileName":"export_test.pdf",
"outputFinal":true,
"outputTimestamp":0
},
"id":"6ca0038f-94ff-4bd9-bdf4-6a35259fd05e"
}
]
}
I expect when I make the post request to get the header specified in the setUp of the mock and the json string corresponding at my file.
feign.Response r = jasperFeignClient.executeReport(headerMap, requestBody);
checkResponseStatut(r.status(), EXECUTION_STEP, documentJasperRequest.getUrlReport(), requestBody);
getCookie(r, cookies);
String execResponse = IOUtils.toString(r.body().asInputStream(), String.valueOf(StandardCharsets.UTF_8));
I have the good return code (200), the cookies that I set, but my response body is messed up and look like this
So when I try to convert it to a java object I have parsing error, because it can't find the begining character of the json.
I even tryed to hard code the json string directly in the body of the stub, but that didn't change a things.
EDIT
When I make a direct call to the stubbed endpoint in my test, the encoding is good
#Test
public void testGetPDF(){
Response response = given().when().post("http://localhost:9561/reportExecutions");
String status = response.jsonPath().get("status");
System.out.println(status);
assertEquals(status, "ready");
}
It's only when I go into the class where the call is made that i got encoding issues.
It looks like your call via the Jasper client is by default setting accept-encodig:gzip (or something similar) and WireMock is therefore returning a gzipped body.
You should be able to resolve the issue by doing one of: a) disabling gzip in WireMock, b) disabling gzip in your client config, or c) ungzipping the body before attempting to parse it.
After updating all my pom dependencies to their latest version solved this encoding issue.
Related
I want to write a JUnit class for a REST endpoint.
This is my REST method. It works fine.
#POST
#Path("create")
#Produces(APPLICATION_JSON)
public String create(#QueryParam("parentId") String parentId, #QueryParam("name") String name) {
//do sth.
return "{\"status\": \"SUCCESS\"}";
}
Now my JUnit test looks like that, which doesn't work, because I don't know how to POST my data in the right way:
#Test
public void testCreate() {
Client client = ClientBuilder.newClient();
WebTarget wt = client.target(REST_MENU_URL + "create");
String queryParams = "parentId=1&name=NEW_JUnit_NEW";
// In the line below, I want to POST my query parameters, but I do it wrong
Response response = wt.request().post(Entity.entity(queryParams, APPLICATION_JSON), Response.class);
// The response has a 500, because the query parameters are all NULL!
assertEquals("Http code should be 200", 200, response.getStatus());
}
So how do I have to change the line with the 'Response' to make it work?
The problem is, that the query parameters (parentId and name) don't get transmitted (response = wt.request().post(...)).
I tried to POST form parameters too, but no success here either. Just like that:
Form form =new Form().param("parentId", "4").param("name", "NEW_JUnit_NEW");
Response response = wt.request().post(Entity.entity(form, APPLICATION_JSON), Response.class);
Thanks,
Bernhard
Check out the Jersey Client documentation, in particular section 5.3.4 on targeting resources.
Query parameters form a part of the URI of the resource, they're not part of the body of the document posted to the resource. You're seeing null in your resource because you're not filling in the query parameters in the URI, you're posting them as the body. You need to tell Jersey to put them in the URI...
WebTarget wt = client.target(REST_MENU_URL + "create").queryParam("parentId", 1).queryParam("name", "NEW_JUnit_NEW");
You'll also need to ensure that your POST request sets the Accept header to allow application/json (by calling the accept(...) method after calling request()) and you're going to need to construct some kind of Entity to pass to the post(...) method - the problem here is that your resource is not consuming the entity body but the client API expects you to send something - this is a code smell which suggests your API is not particularly ReSTful. You can probably get away with some kind of empty body constructed from an empty string. It should look a bit like this...
Response response = wt.request().accept(MediaType.APPLICATION_JSON).post(Entity.text(""))
Alternatively, you could look into converting your API so that it accepts a JSON document and move the query parameters into that document.
I am using jersey API in my project. I stuck in a case that file needs to be downloaded but it's not. My code is as follow
#GET
#Produces("application/download")
public Response downloadFile(){
String data = getDatas();
ResponseBuilder response = Response.ok(data);
response.header("Content-Disposition","attachment;filename=UserData.txt");
response.header("charset", "UTF-8");
return Response.build();
}
I have added all the packages, paths are also fine. No Error came.
When I call this API, data comes in the response. I want this data to be in a file and in a downloadable format.
I also tried #Produces(MediaType.APPLICATION_OCTET_STREAM).
Please correct me if am doing wrong
I think you are not sending the response you have build.
first you're using a ResponseBuilder to build the resposne
ResponseBuilder response = Response.ok(data);
response.header("Content-Disposition","attachment;filename=UserData.txt");
response.header("charset", "UTF-8");
then you are returning the static Response.build() (notice the capital R) object, which is empty
you should return response.build()
also, you should produce octet-stream in your method annotions
#Produces(MediaType.APPLICATION_OCTET_STREAM)
and not
#Produces("application/download")
refer to this question: what's the correct way to send a file from REST web service to client?
I'm trying to invoke a rest call using rest assured. My API accepts, "application/json" as content type and I need to set in the call. I set the content type as mentioned below.
Option 1
Response resp1 = given().log().all().header("Content-Type","application/json")
.body(inputPayLoad).when().post(addUserUrl);
System.out.println("Status code - " +resp1.getStatusCode());
Option 2
Response resp1 = given().log().all().contentType("application/json")
.body(inputPayLoad).when().post(addUserUrl);
The response I get is "415" (indicates that "Unsupported media type ").
I tried invoking the same api using plain java code and it works. For some mysterious reason, I cudn't get it working through RA.
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(addUserUrl);
StringEntity input = new StringEntity(inputPayLoad);
input.setContentType("application/json");
post.setEntity(input);
HttpResponse response = client.execute(post);
System.out.println(response.getEntity().getContent());
/*
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
System.out.println("Output -- " +line);
}
I faced similar issue while working with rest-assured 2.7 version. I tried setting both the contentType and also accept to application/json but it didn't work. Adding carriage feed and new line characters at the end as the following worked for me.
RestAssured.given().contentType("application/json\r\n")
The API seems to be missing to add new line characters after Content-Type header due to which the server is not able to differentiate between the media type and the rest of the request content and hence throwing the error 415 - "Unsupported media type".
Here is the complete POST example using CONTENT_TYPE as JSON.
import io.restassured.http.ContentType;
RequestSpecification request=new RequestSpecBuilder().build();
ResponseSpecification response=new ResponseSpecBuilder().build();
#Test
public void test(){
User user=new User();
given()
.spec(request)
.contentType(ContentType.JSON)
.body(user)
.post(API_ENDPOINT)
.then()
.statusCode(200).log().all();
}
Give a try
given().contentType(ContentType.JSON).body(inputPayLoad.toString)
This might possibly be the case with your test. Try this.
https://github.com/rest-assured/rest-assured/wiki/Usage#avoid-adding-the-charset-to-content-type-header-automatically
Avoid adding the charset to content-type header automatically
By default REST Assured adds the charset header automatically. To
disable this completely you can configure the EncoderConfig like this:
RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false));
As mentioned in previous posts there is a method:
RequestSpecification.contentType(String value)
I did not work for me too. But after upgrade to the newest version (in this moment 2.9.0) it works. So please upgrade :)
I was facing something similar and after some time we noticed the problem was actually coming from the server side. Please check your call on Postman and see if when it's triggered you need to change it from HTML to JSON. If you need to do that, the backend may need to force the response to be in JSON format by adding its content type. Even if it's encoded in JSON you're still may need to do that.
Thats the line of code we added:
header('Content-type:application/json;charset=utf-8');
.
public function renderError($err){
header('Content-type:application/json;charset=utf-8');
echo json_encode(array(
'success' => false,
'err' => $err
));
}
And that's what was happening on the backend:
Hope that can help somehow. :)
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import static org.hamcrest.Matchers.is;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
public class googleMapsGetLocation {
#Test
public void getLocation() {
RestAssured.baseURI = "https://maps.googleapis.com";
given().param("location", "-33.8670522,151.1957362")
.param("radius", "500")
.param("key", "AIzaSyAONLkrlUKcoW-oYeQjUo44y5rpME9DV0k").when()
.get("/maps/api/place/nearbysearch/json").then().assertThat()
.statusCode(200).and().contentType(ContentType.JSON)
.body("results[0].name", is("Sydney"));
}
}
For your First option can you please try adding this header too and sending the request?
.header("Accept","application/json")
I have to create a rest web service where in user access data using url. Url has a query parameter named format which can be either text or file. If user chooses text as format then I have to return text data to browser or if user chooses file as format then return a file for user to download. How can I achieve this?
What I have tried so far (not working) :
#GET
#Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_OCTET_STREAM})
#Path("/some_path")
public Response some_path (#Context HttpServletRequest request) {
String format = null;
if(request.getParameterValues("format") != null && request.getParameterValues("format")[0] != null) {
format = request.getParameterValues("format")[0].toString();
}
else {
format = "text";
}
File file = new File("/some/file/path.txt");
if(format.equals("text")) {
return Response.status(200).entity("sending some text").build();
} else {
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM).header("content-disposition","attachment; filename = result.txt").build();
}
}
With above code format=text works properly but format=file throws HTTP Status 406 null error.
Thanks In Advance
The HTTP status you get is
406 Not Acceptable
To quote Wikipedia:
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
So you client doesn't send a HTTP header of
Accept: text/plain
or
Accept: application/octet-stream
or any combination. Check your client and the header it sends.
You are mixing to approaches. The first, quite popular but not standardized, by providing the hint of the expected result in request's path by using the format parameter (BTW you may use the #QueryParam("format") String format rather than extracting it from ServletRequest). The second approach is the HTTP content negotiation mechanism, in this case using Accept/Content-Type headers. This mechanism is handled by the JAX-RS implementation based on #Produces annotation and providers' classes.
So now, your user is not only required to set the format but also Accept header. It seems that you are using the client which sets the Accept and one of its value is text/plain. That is why it the first case is working, but there is no application/octet-stream or */* (all), so you the JAX-RS expects that the client is not able to process such a content and instead sends him an error 406 Not Acceptable.
The solution here is to remove the #Produces annotation (you are the one who is taking care of the format of the response), or drop the format parameter and let the JAX-RS do his work (probably you will need to register your own provider). If you however stay with your solution then make sure that the correct or no Accept header is send in request (no header means: "I would accept whatever you send").
I recently moved over to Java and am attempting to write some REST tests against the netflix REST service.
I'm having an issue in that my response using rest assured either wants to send a gzip encoded response or "InputStream", neither of which provide the actual XML text in the content of the response. I discovered the "Accept-Encoding" header yet making that blank doesn't seem to be the solution. With .Net I never had to mess with this and I can't seem to find the proper means of returning a human readable response.
My code:
RestAssured.baseURI = "http://api-public.netflix.com";
RestAssured.port = 80;
Response myResponse = given().header("Accept-Encoding", "").given().auth().oauth(consumerKey, consumerSecret, accessToken, secretToken).param("term", "star wars").get("/catalog/titles/autocomplete");
My response object has a "content" value with nothing but references to buffers, wrapped streams etc. Trying to get a ToString() of the response doesn't work. None of the examples I've seen seem to work in my case.
Any suggestions on what I'm doing wrong here?
This has worked for me:
given().config(RestAssured.config().decoderConfig(DecoderConfig.decoderConfig().noContentDecoders())).get(url)
I guess in Java land everything is returned as an input stream. Using a stream reader grabbed me the data I needed.
Until its version 1.9.0, Rest-assured has been providing by default in the requests the header "Accept-Encoding:gzip,deflate" with no way of changing it.
See
https://code.google.com/p/rest-assured/issues/detail?id=154
It works for me:
String responseJson = get("/languages/").asString();