ValidationError in Jersey Client? - java

I'm having trouble getting ValidationErrors from a jersey resource in jersey client. Let me explain.
A function in jersey resource:
#POST
#Produces({ MediaType.APPLICATION_JSON })
#Consumes(MediaType.APPLICATION_JSON)
public LoginInfo login(#NotNull #Valid Login login)
My clientconfig:
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(JacksonFeature.class);
My jersey client:
ClientBuilder.newClient(getClientConfig())
And my function call:
getTarget("/login").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(login), LoginInfo.class);
All works without problems as long i supply valid parameters, when i for example supply null as parameter i get exception:
HTTP 400 Bad Request
But i'm expecting a ValidationError response, because bean validation failed.
Bean validation and error response is working on server, for example with a simple html test, which shows validationerror structure:
<div class="validation-errors"><div class="validation-error"><span class="message">may not be null</span> (<span class="path"><strong>path</strong> = LoginResource.login.arg0</span>, <span class="invalid-value"><strong>invalidValue</strong> = null</span>)</div></div>
How do i get the ValidationError in my jersey client? Do i maybe have to configure it in a special way or maybe i should use a filter?
[edit]
I turned on tracing and when validation fails on server the server sends validationerrors, but it seems jersey client doesn't do anything with it, it converts it to BadRequestException.
6 < 400
6 < Content-Type: application/json
6 < Vary: Accept
[{"message":"may not be empty","messageTemplate":{org.hibernate.validator.constraints.NotBlank.message}","path":"RuleResource.add.arg0.description","invalidValue":""}]

Answering my own question ;-)
It seems jersey client doesn't support validationerrors, so i created a client filter and when status is 400 i get the entity and put the validation errors in a list. Next i create a validationexception which includes the validation errors and just throw it. A bit further up the chain i get a processingexception, in the stacktrace the validationexception can be found and also the validation errors. So i create a nice message from the validation errors and throw another exception, all the way up to my gui code which simply displays the exception message.
Works fine, thanks jersey for the flexibility of filters!

Related

ResponseEntity body lost between two webservice calls

I have a webservice which calls another WS and returns the response from the second WS. It looks like so:
// MyController
public ResponseEntity<Foo> requestFooController(#RequestBody #Valid Bar request) {
return this.myService.requestFooService(request);
}
//MyService
ResponseEntity<Foo> requestFooService(Bar request) {
Buzz improvedRequest = ...
return this.secondWS.secondRequestFoo(improvedRequest);
}
When I call the API through Postman, I receive a HTTP OK response with an empty body. Yet, when I'm in debug mode I can see that the service is returning a ResponseEntity with a body. The headers are not lost though.
I changed my code like so and it works fine:
// MyController
public ResponseEntity<Foo> requestFooController(#RequestBody #Valid Bar request) {
ResponseEntity<Foo> tmp = this.myService.requestFooService(request);
return ResponseEntity.status(tmp.getStatusCode()).body(tmp.getBody());
}
Now through Postman I do have the expected body. However, I don't understand the behaviour. I thought that maybe it's due to the fact that the body is some kind of stream that can be read once or something similar. But from reading the source code I don't see anything that could explain this behaviour.
I'm using the Netflix-stack (so HTTP calls between the two WS are made through a Feign client).
Any idea why I'm getting this result?
EDIT:
More details on my stask:
SpringBoot 1.5.3.RELEASE
Feign 2.0.5
There is a bug that causes the named body of an HTTP MultiPart POST to fail. The symptom of this is that you make a POST request with a body, and Spring-Boot can't match it up to an endoint. The exception I see is:
2019-01-23 15:22:45.046 DEBUG 1639 --- [io-8080-exec-10] .w.s.m.m.a.ServletInvocableHandlerMethod : Failed to resolve argument 3 of type 'org.springframework.web.multipart.MultipartFile'
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
Zuul is doing caching of the request in order to re-try multiple times. In this process, it fails to preserve the named field for the binary body. You may find it working if you preface the request with zuul. So instead of http://myserver.com/myservice/endpoint use zuul in the path: http://myserver.com/zuul/myservice/endpoint
That will effectively avoid the saving of the request and the retry mechanism.
More details are available on this issue in Zuul's GitHub Bug List.

Jax-RS consume ANY Content-Type

I have a REST channel with POST method for uploading a binary content. This can be anything.
The method body is as follows:
#POST
#Path("/")
public Response upload(#Context HttpServletRequest httpRequest) {
It has worked when the client has not set Content-Type header, however if he set it, for example, to application/xml, an error occured:
Unsupported Media Type</pre></p><hr>Powered by Jetty:// 9.3.15.v20161220
I've tried adding wildcard:
#Consumes(MediaType.WILDCARD)
however, it still produces the same error.
How can I make jax-rs to accept ANY content type? Or I'm simply using the wrong tool for my job? I've taken Jax-RS because it can consume beans instantiated by IoC frameworks like blueprint...

How to create RESTful web service client by Jersey2.0 or above

There seems to be many examples about creating RESTful clients by Jersey 1.x, but not Jersey 2.0 or above.
I referred to other questions and the Jersey's web site, but I still cannot create a client for REST due to the differences between Jersey 2.0 and the previous one.
So I'd like to ask some advice.
So far, my coding is like this.
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://localhost:8080/CustomerBack2211/webresources/entities.customer");
Invocation.Builder invocationBuilder = target.request(MediaType.TEXT_XML_TYPE);
Response response = invocationBuilder.get();
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
This produces 406 error.
However, when I tried to test RESTful service by Glassfish server, the test works properly, and the server side class has its #GET methods having #Produces({"application/xml", "application/json"}).
So I don't see why the coding above produces 406 error on a Java application.
(i.e. the client side has #GET methods in the following way)
#GET
#Path("{id}")
#Produces({"application/xml", "application/json"})
public Customer find(#PathParam("id") Integer id) {
return super.find(id);
}
#GET
#Override
#Produces({ "application/xml"})
public List<Customer> findAll() {
return super.findAll();
}
Does any of you see what I'm doing wrong, or could you please suggest an example of a RESTful client?
Any advice will be helpful...thanks in advance!
In addition, I'd appreciate if you would offer information about how to invoke methods like GET, PUT and DELETE with appropriate parameters.
I just needed to put an ID number (i.e. integer values) when I was testing the server side class on Glassfish RESTful test. However, it seems that I need to set "Class" and/or "Entity" values as arguments, but I cannot see any information associated with them on the Jersey website.
For the first block of code:
406 means Not Acceptable.
Look at your request() method target.request(MediaType.TEXT_XML_TYPE). From the Javadoc of request() if states
Invocation.Builder request(MediaType... acceptedResponseTypes)
Start building a request to the targeted web resource and define the accepted response media types.
Invoking this method is identical to:
webTarget.request().accept(types);
So basically, in your request, you are saying that you will only Accept: text/plain. Now look at your resource methods. Look at the #Produces. None of them "produce" text/plain. It's all json or xml. That's why you get the exception. Change the accept to application/xml (or MediaType.APPLICATION_XML) on the client side, and you should no longer get this error.
For the second question: I'm assuming you mean why does it work when you test it from the browser.
If you send a request from the browser by simply typing in the url, it will send out the request with many Accept types. If you have firebug (for FireFox) or the developer tools (for Chrome), if you send out a request, you will see a header similar to
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
You can see application/xml in there. Even if application/xml wasn't there, the wild card */* is there, so basically almost all media types are acceptable as a return type when working in the browser.
For your last question:
Look at the API for SyncInvoker, which Invocation.Builder extends from. You will see different overrloaded put and post methods, most of which, as you mentioned accept an Entity.
There are a few different ways to build an Entity, all of which use one of the static methods. Here are some
Entity.entity( body, mediaType )
Entity.json( body )
Entity.xml( body )
And many more (see the Entity link above). But all of these static method return an Entity. So we could do something like
// resource method
#POST
#Consumes(MediaType.APPLICATION_XML)
public Response getResponse(Customer customer) { ... }
// some model class
#XmlRootElement
public class Customer { ... }
// client request
Customer customer = new Customer();
Response response = target.request().post(Entity.xml(customer));
Internally, the Customer will get converted to XML. If you used Entity.json is would get converted to JSON, BUT you need to make sure you have a JSON provider dependency. Jersey will not come with one by default. See more at Support for Common Media Type Representations
Also note, with your method find, when you try and make a request to the method, the request should end with an integer value, as that's the type specified for the {id} path parameter.

Post request to Jersey from Laravel

I have a simple REST API created with Jersey on Glassfish server.
On the other hand, I have a Laravel application that uses this API. Now, I have several GET routes on Jersey app, and this works with no problem. I used it with curl and everything went well.
At the end, I have one post request that I should send. Form consists of about 50 fields, and I'm not sure how to triger it.
When I put the url to post method into form action attribute, I get this message
HTTP Status 415 - Unsupported Media Type
I have tried with
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
And with
#Consumes(MediaType.APPLICATION_JSON)
And I get the same error with both
I haven't done this but it, but be sure to include the form parameters in the method signature
#POST
#Consumes("application/x-www-form-urlencoded")
public void post(#FormParam("name") String name) {
// Store the message
}
as described here

JAX-RS how to handle wrong content-type

I have a REST service with a #POST method which #Consumes("application/xml)".
However, if I make a POST request from my browser and I don't add
Content-Type: application/xml
header to the request, I get an exception in my jboss
Failed executing POST : org.jboss.resteasy.spi.UnsupportedMediaTypeException: Cannot consume content type
How is my servlet supposed to handle such cases?
You can implement an ExceptionMapper for the UnsupportedMediaTypeException and choose to handle it however you wish. You're seeing this exception because you don't have a handler for it and resteasy is doing its out of the box handling.

Categories