I'm building a Rest Client using jersey-client 2.19:
public ReleaseEntity createRelease(ReleaseEntity newRelease, int workspaceId) {
Releases wrapper = new Releases();
wrapper.setData(Arrays.asList(newRelease));
WebTarget target = client.target(urlPrefix)
.path(AgmUrls.getReleasesUrl(workspaceId));
wrapper = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(wrapper, MediaType.APPLICATION_JSON))
.readEntity(Releases.class);
return wrapper.getData().get(0);
}
The client is initialized in the constructor
this.client = ClientBuilder.newClient();
The problem is that, in case of bad response the post call does not throw an exception, neither explicit nor runtime.
Should I do this manually, or am I missing something?
This question is quite dated, but better prevent others to repeat the same mistake...
Instead of
result = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(input, MediaType.APPLICATION_JSON))
.readEntity(Releases.class);
which has post(entity) return a Response on which readEntity is called, better use overloaded post(entity, responseType) which will throw WebApplicationException on Error-Statuscodes.
// throws runtime exception derived from WebApplicationException
// on error-statuscodes
result = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(input, MediaType.APPLICATION_JSON), Releases.class);
Every http method in JAX-RS has such overloaded methods for reading either Responses or representation objects. Reading representation objects is highly advised to consume potential response bodies in any case.
// consumes response as string and guarantees to close the http call.
// A no-arg delete(); would be a potential leak!
target.request().delete(String.class);
Unfortunately, when response-headers must be read, it is still required to read Response instead of the representation objects.
The framework should not throw an exception. The user should handle the response however they see fit. This is the same with any client. The Response object will contain all the context you need to handle the response however you see fit.
So what you should do is get the Response first
Response response = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(wrapper, MediaType.APPLICATION_JSON));
Then you can check the status
int status = response.getStatus();
Then handle the status
if (status == 200) {
wrapper = response.readEntity(Releases.class);
...
} else {
handleOtherStatus();
}
If you do not get the Response first, then you have no idea what the actual problem is, as readEntity(...) will fail (as there it's not the body you are expecting), and throw a different exception. With the Response at least you have some context if you want to tell the user what actual problem is.
Related
This question already has answers here:
Spring Resttemplate exception handling
(16 answers)
Closed 2 years ago.
I'm reading a ResponseEntity from a webService using the Java (Spring 2.0) code below:
public ResponseEntity<?> getTemplate() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<?> myResponse= restTemplate.postForEntity(
myUrl,
myRequestObj,
MyResponseObj.class);
return myResponse;
}
However, if the myUrl webservice returns HttpStatus.BAD_REQUEST (400) , this is not assigned to myResponse and an error is thrown, so there is no ResponseBody and I need to wrap the request in a try catch block. Is this correct or is there a way round this?
Also, does this mean that the myUrl webservice should never intentionally (programatically) set the HttpStatus of myResponseObj to HttpStatus.BAD_REQUEST? So,even if myRequestObj contains bad data the myUrl webService should still set the response status to something in the 200's ie HttpStatus.NO_CONTENT. Any comments welcome on how to do this correctly.
Is this correct or is there a way round this?
The behaviour you describe is defined by spring's detault error handler, which throws an HttpClientErrorException in case of a status code in the 400-499 range, an HttpServerErrorException in case of a status code in the 500-599 range and an UnknownHttpStatusCodeException if the status code is unknown. To handle such error codes you can either catch the exceptions or register a custom exception handler as described here.
Also, does this mean that the myUrl webservice should never intentionally (programatically) set the HttpStatus of myResponseObj to HttpStatus.BAD_REQUEST?
According to RFC 7231, the status code 400 is used to indicate that the server can't or won't process the request because of an error the client made creating the request (e.g., malformed request syntax, invalid request
message framing, or deceptive request routing). Therefore you are free to use that status code to indicate such behaviour to the client.
I'm trying to unmarshal a json response from a REST service into a Java pojo but am unable to do so.
How do I get the response body out of HttpResponse as a string?
How do I get the response body unmarshalled directly into a Java pojo?
Is the GET request made in the below code asynchronous?
I have tried looking into the Akka documentation and other sites but cannot find the answers anywhere.
final Http http = Http.get(actorSystem);
CompletionStage<HttpResponse> response =
http.singleRequest(HttpRequest.GET("http://127.0.0.1:8081/orders/24"));
HttpResponse httpResponse = response.toCompletableFuture().get();
Order s =
Jackson.unmarshaller(Order.class)
.unmarshal(
httpResponse.entity(),
ExecutionContexts.global(),
ActorMaterializer.create(actorSystem)
).toCompletableFuture()
.get();
System.out.println("response body: " + s); `
How do I get the response body out of HttpResponse as a string?
In a blocking way (not recommended, but to continue from your code snippet):
HttpResponse httpResponse = response.toCompletableFuture().get();
Strict strict = httpResponse.entity().toStrict(TIMOUT_MS, mat).toCompletableFuture().get();
String body = strict.getData().utf8String();
A better non-blocking way is to do it asynchronously:
response.thenCompose(response ->
response.entity().toStrict(TIMEOUT, mat)
).thenApply(entity ->
entity.getData().utf8String())
).thenApply(body ->
// body is a String, do some logic on it here...
);
The materializer (mat) can be created like so if you don't already have one (the type comes from the akka-stream library, so you'll need a dependency on it):
Materializer mat = ActorMaterializer.create(actorSystem);
How do I get the response body unmarshalled directly into a Java pojo?
Haven't tested this, but should do the trick according to the docs:
Unmarshaller<HttpEntity, Order> unmarshaller = Jackson.unmarshaller(Order.class);
response.thenCompose(response ->
response.entity().toStrict(TIMEOUT, mat)
).thenApply(entity ->
unmarshaller.unmarshal(entity, mat)
)
Is the GET request made in the below code asynchronous?
Yes it is. Unless you block on the returned CompletionStage, as you are doing with response.toCompletableFuture().get().
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 want to do something to sign up users with spark+java+hibernate+postgres
This is my code:
post("/registrar", (request, response) -> {
EntityManagerFactory emf = Persistence.
createEntityManagerFactory("compradorcitoPU");
EntityManager em = emf.createEntityManager();em.getTransaction().begin();
em.persist(u);
em.getTransaction().commit();
em.close(); return null; });
but this error shows up:
INFO spark.webserver.MatcherFilter - The requested route
[/registrarnull] has not been mapped in Spark
I had a similar problem. The items I'm returning are large and I wanted to write them out over stream. So, my software looked like this:
post("/apiserver", "application/json", (request, response) -> {
log.info("Received request from " + request.raw().getRemoteAddr());
ServerHandler handler = new ServerHandler();
return handler.handleRequest(request, response);
});
In my handler, I got the raw HttpResponse object, opened its OutputStream and wrote over it like so:
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.raw().getOutputStream(), records);
Since I knew I had written over the OutputStream what the caller had asked for at that point (or an error), I figured I could just return null. My program worked fine. Spark would route the request to my handler as expected. And, since I was writing over the raw OutputStream, I was getting back what was expected on the client side. But, I kept seeing the message '/apiserver route not defined' in my server logs.
In looking at the Spark documentation, it says:
The main building block of a Spark application is a set of routes. A route is made up of three simple pieces:
A verb (get, post, put, delete, head, trace, connect, options)
A path (/hello, /users/:name)
A callback (request, response) -> { }
Obviously Spark does not know what you wrote over the raw HttpResponse and as a web-server, you should be providing some response to callers. So, if your response is null, you haven't fulfilled the requirements of providing a callback and you get the error that there's no map found even if Spark behaved as expected otherwise. Just return a response (null is not a response, "200 OK" is) and the error will go away.
[Edit] Spelling and grammar.
do not "return null" instead return the empty string or something
As explained in the comments of this issue, SparkJava considers that returning null means the route has not been mapped and therefore it logs the error message and replies a response with 404 status.
To avoid such behaviour you have to return a String (possibly empty).
The error message will disappear and a response with the String as body and 200 status will be replied.
In my case, I had to implement the options request to please the preflight CORS check:
options("/*", (request,response)->{
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if(accessControlRequestMethod != null){
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
});
I see a weird issue. I have two webapps. One for the rest webservice that we exposed using Jersey. Another has the JSF Front End implementation which calls above webservice to fetch the details. We are using Tomcat as a container.
The issue that i am facing is when i call a rest webservice, i throw WebApplicationException like this
catch (CustomExceptions e) {
ResponseBuilder response = new ResponseBuilderImpl();
response.status(500);
response.entity(e.getStackTrace());
throw new WebApplicationException(e, response.build());
}
And on the other hand on FE webapps, i do the following:
try {
r.get(MyClass.class);
return "SUCCESS";
} catch (WebApplicationException wae) {
return "FAILURE";
} catch (UniformInterfaceException wae) {
return "FAILURE";
}
Here in the catch block i was expecting the WebApplicationException but its throwing UniformInterfaceException which is weird. Also if it throws UniformInterfaceException, It does not maintain the stacktrace. Even the response that i passed in from rest call is lost. Can somebody help me how can i get the original stacktrace from the rest call?
Thanks in advance.
As specified in both the user guide and the api documentation, a UniformInterfaceException is the expected result when you attempt to retrieve a specific entity by type but receive a non-200-series response. The WebApplicationException is a convenience exception for your use on the server side to embed custom responses in an exception.
Nothing is "lost". Keep in mind you're making an HTTP request here. If you want to inspect the status code and response yourself, use
ClientResponse response = r.get(ClientResponse.class);
if (response.getStatus() == <expected status code>) [
response.getEntity(MyClass.class);
// do happy path stuff
} else {
// bad stuff here
}
Alternately, assume everything will go well, and only check the response on exceptions:
try {
r.get(MyClass.class);
} catch (UniformInterfaceException e) {
ClientResponse response = e.getResponse();
// do whatever with the raw response
}
I had same issue before. I think it's because Jersey WebResource.get method throws only UniformInterfaceException which is not in the inheritance hierarchy of WebApplicationExcpetion. What I did is to abandon Jersey client side api but use plain ApacheHttpClient. Will be able to get the right stacktrace in the httpResponse contents.
Hope this helps.
Thanks, Ryan. I tried your solution. Very nice.