I have a REST API with Jersey and the requests traces is like this:
Información: 5 * Server out-bound response
5 < 200
5 < Content-Type: application/json
5 < X-Jersey-Trace-000: accept root resource classes: "/vales"
5 < X-Jersey-Trace-001: match path "/vales" -> "/application\.wadl(/.*)?", "/selacservicios(/.*)?", "/proveedores(/.*)?", "/vehiculos(/.*)?", "/empresas(/.*)?", "/vales(/.*)?", "/ok(/.*)?"
5 < X-Jersey-Trace-002: accept right hand path java.util.regex.Matcher[pattern=/vales(/.*)? region=0,6 lastmatch=/vales]: "/vales" -> "/vales" : ""
5 < X-Jersey-Trace-003: accept resource: "vales" -> #Path("/vales") com.grupogimeno.senda.siccagest.services.rest.ValeCompraResource#3f494991
5 < X-Jersey-Trace-004: match path "" -> ""
5 < X-Jersey-Trace-005: accept resource methods: "vales", GET -> com.grupogimeno.senda.siccagest.services.rest.ValeCompraResource#3f494991
5 < X-Jersey-Trace-006: matched resource method: public java.util.Map com.grupogimeno.senda.siccagest.services.rest.ValeCompraResource.getValesCompraDeUsuario(java.lang.String,java.lang.Integer)
5 < X-Jersey-Trace-007: matched message body writer: java.util.LinkedHashMap#b690aa46, "application/json" -> com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider#12b315ef
5 <
{"success":true,"data":[{"fGeneracion":1428579118957,"numValeCompra":"001562","abreviaturaEmpresa":"AGU. NUMANCIA","nombreProveedor":"MAJIMSA VALENCIA, S.A."},{"fGeneracion":1428579081230,"numValeCompra":"005647","abreviaturaEmpresa":"A.VINAROS-UTE","nombreProveedor":"EXCAV BABILONI, S.A."},{"fGeneracion":1428579081230,"numValeCompra":"005647","abreviaturaEmpresa":"A.VINAROS-UTE","nombreProveedor":"EXCAV BABILONI, S.A."}]}
Is there a way to print the JSON response prettier? Like this:
{
"success": true,
"data": [
{
"fGeneracion": 1428579118957,
"numValeCompra": "001562",
"abreviaturaEmpresa": "AGU. NUMANCIA",
"nombreProveedor": "MAJIMSA VALENCIA, S.A."
},
{
"fGeneracion": 1428579081230,
"numValeCompra": "005647",
"abreviaturaEmpresa": "A.VINAROS-UTE",
"nombreProveedor": "EXCAV BABILONI, S.A."
},
{
"fGeneracion": 1428579081230,
"numValeCompra": "005647",
"abreviaturaEmpresa": "A.VINAROS-UTE",
"nombreProveedor": "EXCAV BABILONI, S.A."
}
]
}
This is the filter in web.xml:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
</init-param>
Thanks.
You can configure the ObjectMapper to configure(SerializationFeature.INDENT_OUTPUT, true);. You can configure it in a ContextResolver, as seen here.
Note though that this will format the actual response, and not just the logging. It seems the logging just prints the response "as-is", so I don't know how you would have one without the other (except by writing your own logger)
Use com.google.gson.Gson -
The code will then be -
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(JsonObject);
LOGGER.info("JSON prettier response: "+json);
Related
So I am trying to create a simple webservice post that consumes json. But I am geting the error RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003065: Cannot consume content type
My webservice:
#POST
#Produces(MediaType.APPLICATION_XML)
#Path("teste1")
#Consumes(MediaType.APPLICATION_JSON)
public Response teste1(String product) {
String result = "Product created : " + product;
System.out.println("resultado");
System.out.println(result);
return Response.ok() //200
.entity("<erro> none </erro>")
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.header("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With").build();
}
I also tried to do:
#Consumes("application/json")
But I am getting the same error. I can make it work if I do:
#Consumes("*/*")
But I can't understand why it doesn't work when I say it consumes json. To test the webservice I am using https://apitester.com/. With the folowing Post Data:
{
"key" : "value",
"array" : [
{ "key" : 1 },
{ "key" : 2, "dictionary": {
"a": "Apple",
"b": "Butterfly",
"c": "Cat",
"d": "Dog"
} },
{ "key" : 3 }
]
}
In general the
#Consumes("application/json")
specifies a content media type which webservice can handle.
But also you may need to explicitly specify an appropriate type in the Content-Type header for your request.
I am not familiar with the https://apitester.com but probably it does not send the Content-Type header by default
In such case your server can treat the request body as a plain text, for instance. That request would not be routed to your endpoint, because it is not designed for the plain text.
Setting the #Consumes(*/*) fixes that problem, because the wrong media type matches that pattern as well.
Could you please ensure that you sends the Content-Type: application/json with your POST request?
I am trying to perform a status update PUT request. The following example returns 200 in Postman:
URL:
http://www.example.com/users/3/status?seId=1&dt=2016-11-01T00:00:00Z
HEADERS:
Content-Type:application/json
charset:utf-8
Authorization:Bearer LONG_TOKEN_HERE
BODY:
{ "status": 1 }
This is the structure of my Retrofit 2 request:
#PUT("users/{id}/status")
Call<Void> updateEventStatus(#Header("Authorization") String token,
#Path("id") int id,
#Query("seId") int seId,
#Query("dt") String dateTime,
#Body Status status);
The request's URL is the same as in Postman and so are the headers, so I suspect it is related to the body. Status is just a wrapping class with a single int field named status, which I created by following this answer (I did the same with credentials and it works well). I also tried making the status in body of type int but it results in Bad Request as well.
Any idea what could be the difference between the Postman request and the Retrofit 2 request? Thanks!
EDIT: This is the originalRequest in Retrofit 2:
Request{method=PUT, url=http://example.com/api/users/3/status?seId=0&dt=2016-10-04T05:30:00Z, tag=null}
headers: Authorization: Bearer LONG_TOKEN_HERE
contentType: application/json; charset=UTF-8
content:
0 = 123
1 = 34
2 = 115
3 = 116
4 = 97
5 = 116
6 = 117
7 = 115
8 = 34
9 = 58
10 = 51
11 = 125
Translated content:
{"status":3}
Eventually, it was a server-side bug (I received a false seId at first, and then tried to PUT using an seId that doesn't exist).
I am currently writing some acceptance tests to a jersey 2-0 webservice (json) before refactoring some methods in the project, and i have stumbled upon a problem with asserting blank-space strings from the webservice.
I get the following output from my webservice:
"Boxes": [
{
"id": 1,
"title": " ", //Yes this is a white-space
"genre": "genre",
"info": "some info",
"rating": "3",
"artist": "Artist 1"
}
],
And i convert my response to JSONObject from the webservice as following:
public static JSONObject responseToJsonObject(HttpResponse httpResponse) throws IOException {
String responseString = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
return new JSONObject(responseString);
}
Then in my test, is do normal asserts like so:
//* http request creation omitted *//
JSONObject jsonResponse = JsonTranslator.responseToJsonObject(httpRequest)
JSONArray boxes = jsonResponse.getJSONArray("boxes ");
JSONObject result = boxes .getJSONObject(0);
assertEquals(" ", result.getString("title"));
Problem is that this last assert fails.
org.junit.ComparisonFailure:
Expected :
Actual :
And i probably know why, the byte arrays yield two different results. One has a byte of [32] (the one created in java) and the one from the webservice has [-62,-96]
I have never encountered this problem before. And if i try to convert without the "UTF-8" in the EntityUtils method i get an even worse result (Â byte array: [-61,-126,-62,-96]
I can see my webservice's content-type header is properly set as application/json
Can anyone explain what happens here?
The character you are getting there is not a "normal" space. It's a no-break space. The same character that in HTML would be described with .
Its unicode value is '\u00A0'. Its UTF-8 representation is C2 A0, which is what you are getting in your byte array.
I believe if you tried the following assert it would work:
assertEquals("\u00A0", result.getString("title"));
I have a Spring HATEOAS Resource such that ModelResource extends Resource<Model>.
In a #RestController I have a method to create new Models:
#RequestMapping(value = "", method = POST)
public ResponseEntity<ModelResource> postModel() {
Model model = service.create().model;
ModelResource resource = assembler.toResource(model);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(resource.getLink("self").getHref()));
return new ResponseEntity<>(resource, headers, CREATED);
}
The created ModelResource returned from the above method is HAL-encoded:
$ curl -v -XPOST localhost:8080/models
> POST /models HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 201 Created
< Date: Sun, 25 Jan 2015 11:51:50 GMT
< Location: http://localhost:8080/models/0
< Content-Type: application/hal+json; charset=UTF-8
< Transfer-Encoding: chunked
< Server: Jetty(9.2.4.v20141103)
<
{
"id" : 0,
"_links" : {
"self" : {
"href" : "http://localhost:8080/models/0"
}
}
}
In the same controller also have a method to list Models.
#RequestMapping(value = "", method = GET)
public List<ModelResource> getModels() {
return service.find().stream()
.map(modelProxy -> assembler.toResource(modelProxy.model))
.collect(Collectors.toList());
}
For some reason, this method returns plain JSON, not HAL:
$ curl -v localhost:8080/models
> GET /models HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 25 Jan 2015 11:52:00 GMT
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Server: Jetty(9.2.4.v20141103)
<
[ {
"id" : 0,
"links" : [ {
"rel" : "self",
"href" : "http://localhost:8080/models/0"
} ]
} ]
Why does the first method return HAL, and the second returns plain JSON?
How can I specify consistent behavior?
I've read about #EnableHypermediaSupport, but I don't have it set anywhere in my code.
From this GitHub issue:
That works as expected. HAL defines the top level resource having to
be a document. Thus a plain List by definition cannot be a HAL
document. We've restricted the HAL customizations to only be applied
if the root object to be rendered is ResourceSupport or a subtype of
it to prevent arbitrary objects from getting HAL customizations
applied. If you create a ResourceSupport instead of a List, you should
see a correct HAL document be rendered.
The HAL Primer provides more detail and examples of what such a top-level wrapping resource looks like. In Spring HATEOAS, you use Resources<T> to represent such a wrapper, which is itself a Resource<T> with a self rel:
#RequestMapping(value = "", method = GET)
public Resources<ModelResource> getModels() {
List<ModelResource> models = service.find().stream()
.map(modelVertex -> assembler.toResource(modelVertex.model))
.collect(Collectors.toList());
return new Resources<>(models, linkTo(methodOn(ModelController.class).getModels()).withRel("self"));
}
The returned Resources<T> type is then encoded into a document with embedded documents:
$ curl -v localhost:8080/models
> GET /models HTTP/1.1
> User-Agent: curl/7.32.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 25 Jan 2015 13:53:47 GMT
< Content-Type: application/hal+json; charset=UTF-8
< Transfer-Encoding: chunked
< Server: Jetty(9.2.4.v20141103)
<
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/models"
}
},
"_embedded" : {
"modelList" : [ {
"id" : 0,
"_links" : {
"self" : {
"href" : "http://localhost:8080/models/0"
},
"links" : {
"href" : "http://localhost:8080/models/0/links"
}
}
} ]
}
}
As mentioned above, Resources<T> extends ResourceSupport just like Resource<T>. So you could create a ResourceAssembler for Resources<ModelResource> in order to avoid having to creating the self link by hand, and to generally encapsulate Resource<ModelResource> creation.
This answer suggests that HAL rendering is enabled by Spring Boot if it is available, which explains resources are being rendered HAL when possible.
I expect Spring is automatically choosing application/hal+json as the default Content-Type for all ResponseEntity instances.
I'm retrieving data from a rest service and I'm using JSON to map the JSON response to java POJOs. All works fine, except the service will return a different JSON result for invalid calls, which is not mappable to the POJO:
{
"error":[
{
"code": 1,
"message":"Parameter is invalid."
}
]
}
UPDATE:
The response format for a valid call looks something like this:
persons: {
personCount: 14
person: [
{
firstname: "Michael"
name: "Bolton"
}
]
}
I'm mapping the response like this:
Person person = mapper.readValue(in, Person.class);
I've looked through the API and some SO and blog-postings, but I didn't find a hint on how to approach this so far. Can you give me a hint?