apache wink server stub return null,the rest client throw exception - rest

Server stub
#GET
#Path("/user/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser(#PathParam("id") String id){
User user = myService.getUserById(id);
if (user!= null){
return user;
}
return null;
}
==============================
Client stub
User response = WinkRestClient.resource(path).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).get(User.class);
throw exception:
Exception in thread "main" java.lang.RuntimeException: A javax.ws.rs.ext.MessageBodyReader implementation was not found for class com.test.entity.User type and application/octet-stream media type. Verify that all entity providers are correctly registered. Add a custom javax.ws.rs.ext.MessageBodyReader provider to handle the type and media type if a JAX-RS entity provider does not currently exist.
at org.apache.wink.client.internal.handlers.ClientResponseImpl.readEntity(ClientResponseImpl.java:122)
at org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:65)
at org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:52)
at org.apache.wink.client.internal.ResourceImpl.invoke(ResourceImpl.java:196)
at org.apache.wink.client.internal.ResourceImpl.get(ResourceImpl.java:303)
at com.wolianw.sale.bl.ClResourceTest.main(ClResourceTest.java:14)
when server stub return null ,the client throw this exception
what can I do,let the client don't throw exception,let response [User = null]

You need to do exactly what is described in error. Create MessageBodyReader for MediaType.APPLICATION_OCTET_STREAM.
If you have no payload in your response and that is the case when you return null, the client is looking for provider registered for MediaType.APPLICATION_OCTET_STREAM, no matter what accept header you set in your request and what is actual header of the response.
You may use the Jersey client if you don't like this behavior, I do.

Related

Customising Spring Boot Exception Handling to Prevent Stacktraces Being Returned in Rest Response

How do I configure my spring boot service so that errors such as 500 don't potentially leak implementation details such as stacktraces.
{
"timestamp": "2019/05/01 15:06:17",
"status": 500,
"error": "Internal Server Error",
"message": "Type definition error: [simple type, class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.Collections$UnmodifiableRandomAccessList[0]->........)",
"path": "/api/test"
}
Note: here the stacktrace is in the message and not the exception part of the json.
As you can see I am already formatting the timestamp with:
#Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
private static final String TIMESTAMP = "timestamp";
#Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
//Let Spring handle the error first
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
//Format & update timestamp
Object timestamp = errorAttributes.get(TIMESTAMP);
if(timestamp == null) {
errorAttributes.put(TIMESTAMP, dateFormat.format(new Date()));
} else {
errorAttributes.put(TIMESTAMP, dateFormat.format((Date)timestamp));
}
return errorAttributes;
}
}
But I need to handle the message too.
If this 500 was the only error I could just do:
errorAttributes.put("message", "Server error. Contact support.");
However, all the errors go through here and that would override all the messages.
I could check if the status is 500 and only modify it then. However, there are other errors that can be generated that also might leak stacktraces.
Using #RestControllerAdvice seems to require knowing every exception that is generated and having an #ExceptionHandler for each and knowing which status code to respond with.
Is there a cleaner way to handle this?
It may not be the "cleanest" approach, but with projects I've been on we had a "standard format" for our Error Responses across projects, so we had a custom object with the fields that matched our orgs standard (HttpStatus, Reason, ect.) that extended RuntimeException. Then in our controllers, services, repos, ect we would catch exceptions and create this object accordingly and throw the custom one up instead. Based upon where it happened in the app (repo, service, controller, ect.) we could give our own custom verbage to it, but still log out the full exception in our server logs so we could investigate later
For example if we caught an error in our repository we would create our custom error object, set the Reason to DB unavailable (really all the consumer needs to know), set the status to HttpStatus.SERVICE_UNAVAILABLE (we tracked these with reasons and httpstatus with enums to keep status the same across modules), and throw the custom object up to the controller to be returned.
Sorry if this was a longwinded answer that may not give you what you want, I'm not too familiar with how you're trying to do it so figured I'd just give an example of other methods. I'll put some sample code as well
Custom Exception:
data class MyException(
val reason: String,
val httpStatus: HttpStatus? = null
) : RuntimeException(reason)
Method for creation:
fun createApiException(errorCode: ErrorCodeEnum) = MyException(
reason = errorCode.reason,
httpStatus = errorCode.httpStatus,
)
Spring-boot provides us with a standard method to handle exceptions using spring aop concept. You can use the #ControllerAdvice and #Exceptionhandled annotations to handle exceptions from a spring-boot rest endpoint so that a custom exception is always thrown from a rest endpoint with proper error code and error response.
The #ResponseStatus() annotation can be used to customize the response code being thrown.
For example consider the custom exception :
#ResponseStatus(HttpStatus.NOT_FOUND)
public class DataNotFoundException extends RuntimeException {
public DataNotFoundException(String exception) {
super(exception);
}
}
We can throw this error from a rest GET mapping when a data is not found like :
#GetMapping("/trains/{id}")
public Resource<Student> retrieveTrains(#PathVariable long id) {
Optional<Trains> trains = trainRepository.findById(id);
if (!train.isPresent())
throw new DataNotFoundException("id-" + id);
Resource<Trains> resource = new Resource<Trains>(train.get());
ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllTrains());
resource.add(linkTo.withRel("all-trains"));
return resource;
}
Default error response provided by Spring Boot contains all the details that are typically needed.
However, you might want to create a framework independent response structure for your organization. In that case, you can define a specific error response structure.
For example :
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
super();
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
To use this error node we use :
#ControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(DataNotFoundException.class)
public final ResponseEntity<ErrorDetails> handleUserNotFoundException(DataNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(DataNotFoundException.class) indicates that this
method would handle exceptions of the specific type.
new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND) - Create an
error response object and return it with a specific Http Status.
For a more generalized exception handler you can define a method that handles exception of the type Exception.class, that way you don't have to know every exception.
Like :
#ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorDetails> handleAllExceptions(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
Reference from : https://www.javaguides.net/2019/02/spring-boot-2-angular-7-crud-example-tutorial.html

JAX-RS AsyncResponse.resume() with Location header

UPDATE
Some more digging showed that thrown Exceptions were dropped and the actual problem is that an injected UriInfo could not be resolved in the AsyncResponse's thread!
Accessing #Context UriInfo uriInfo; during AsyncResponse.resume() gives the following LoggableFailure's message:
Unable to find contextual data of type: javax.ws.rs.core.UriInfo
ORIGINAL
According to RFC 7231 HTTP/1.1 Semantics and Control, a POSTshould return 201 CREATED and supply the new resource's location in the header:
the origin server
SHOULD send a 201 (Created) response containing a Location header
field that provides an identifier for the primary resource created
(Section 7.1.2) and a representation that describes the status of the
request while referring to the new resource(s).
When writing a synchronous REST Server, the javax.ws.rs.core.Responseoffers the Response.created() shorthand which does exactly that.
I would save the new entity, build an URI and return
return Response.created(createURL(created)).build();
However, when I switch to an asynchronous approach utilizing a
#Suspended javax.ws.rs.container.AsyncResponse
the HTTP request on the client will hang infinitely:
#POST
public void createUser(#Valid User user, #Suspended AsyncResponse asyncResponse) {
executorService.submit(() -> {
User created = userService.create(user);
asyncResponse.resume(
Response.created(createURL(created)).build()
);
});
}
Through trial-and-error I found out that the modified location header is responsible.
If I return my entity and set the 201 Created, without touching the header, the request will eventually resolve:
#POST
public void createUser(#Valid User user, #Suspended AsyncResponse asyncResponse) {
executorService.submit(() -> {
User created = userService.create(user);
asyncResponse.resume(
Response.status(Status.CREATED).entity(created).build() //this works
//Response.created(createURL(created)).build()
);
});
}
So what's the problem? Am I misunderstanding the concepts?
I am running RestEasy on GlassFish4.1
If you need more information, please comment!
edit
As soon as I change any link or the header, the request will hang.
In case anyone ever has the same problem:
The problem was that I created the location header through an injected #Context UriInfo uriInfo using its .getAbsolutePathBuilder().
The approach was working in a synchronous server because the thread which accessed the UriInfo still had the same Request context.
However, when I switched to an async approach, the underlying Runnable which eventually had to access uriInfo.getAbsolutePathBuilder() was NOT within any context - thus throwing an exception which halted further execution.
The workaround:
In any async method which should return a location header, I .getAbsolutePathBuilder() while still within the context. The UriBuilder implemantion can then be used within the async run:
#POST
public void createUser(#Valid User user, #Suspended AsyncResponse asyncResponse) {
UriBuilder ub = uriInfo.getAbsolutePathBuilder();
executorService.submit(() -> {
User created = userService.create(user);
asyncResponse.resume(
Response.created(createURL(ub, created)).build()
);
});
}
private URI createURL(UriBuilder builder, ApiRepresentation entity) {
return builder.path(entity.getId().toString()).build();
}

#BeanParam gives exception A message body reader for Java class was not found

I am trying to make a jersey based web service. In this if i take input params using #FormParam it works fine:
#POST
#Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, "application/x-www-form-urlencoded"})
#Path("/registeruser")
public Response registerUser(#FormParam ("email") String email,#FormParam ("name") String name ){
System.out.println("Inside register device");
System.out.println("registered" + email);
return null;
}
but when I try using #BeanParam it does not works and gives me an exception
#POST
#Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, "application/x-www-form-urlencoded"})
#Path("/registeruser")
public Response registerUser(#BeanParam UserForm userForm ){
System.out.println("Inside register device");
service.registerUser(userForm);
System.out.println("registered" + userForm.getEmail());
return null;
}
A message body reader for Java class com.stc.dms.forms.UserForm, and Java type class com.stc.dms.forms.UserForm, and MIME media type application/octet-stream was not found.
You don't need to use #BeanParam to pass an object as input. Just pass it like this :
#POST
#Path("register")
#Consumes(MediaType.APPLICATION_JSON)
public Response registerUser(UserForm dto) {
// ...
}
Just make sure to include the libraries for producing/consuming json. If the client is in javascript you don't need anything else (just use JSON.stringify() on your form object), for the server add some json libraries such as Jackson
EDIT :
If you want to stick with #BeanParam, take a look at this tutorial here. Basically it seems that you don't need to specify the mediatype, as Jersey will handle that for you.

jersey 2.0 jaxrs RI - return json string on exception

I am creating a REST service using jersey 2.0. I am extending WebApplicationException
Method raising a particular exception
if(json.equals("") || json.equals(" ")) {
throw new ArgumentException("bad post data");
}
public class ArgumentException extends RestException {
.....
public ArgumentException(String message) {
super(Status.BAD_REQUEST,message);
}
}
public class RestException extends WebApplicationException {
...........
public RestException(Status status, String message) {
super(Response.status(status)
.entity(message)
.type("text/plain")
.build());
/*
super(Response.status(status)
.entity(new ErrorBean(status.getStatusCode(),message))
.type(MediaType.APPLICATION_JSON)
.build()); */
}
ErrorBean is a POJO
The method that returns error as plain string inside RestException works (right http code 400 and message). However when I try to pass the ErrorBean POJO and use MediaType.APPLICATION_JSON in response I get an error saying "Headers have already been sent" with http error code 500 (so some internal problem with plumbing) and empty response.
I have also looked at this question Returning JSON or XML for Exceptions in Jersey
How can I return the exception with code and message as a JSON like
{"code" : 400, "message" : .... }
Update
I have received answer on SO as well as jersey users mailing list. steps are
A non AJXB POJO does not need any annotations
Register JacksonFeature in your application
ResourceConfig rc = new ResourceConfig().packages("test").register(JacksonFeature.class);
You need to register JacksonFeature in your Application/ResourceConfig, i.e.:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jackson")
.register(JacksonFeature.class)
// No need to register this provider if no special configuration is required.
.register(MyObjectMapperProvider.class);
Take a look at the documentation for Jackson support in Jersey and also at the example.

GWT requestfactory: How to catch the exception i thrown in Locator at server side?

At client side:
factory.find(proxyId).fire(new Receiver<P>()
{
#Override
public void onSuccess( P response )
{
proxy = response;
...
}
#Override
public void onFailure( com.google.web.bindery.requestfactory.shared.ServerFailure error )
{
Window.alert( error.getMessage() );
}
}
at server side i use an Locator like below:
public class myLocator extends Locator<T, String>
{
#Injector LocatorHook hook;
#Override
public T find( Class<? extends T> clazz, String id )
{
T result = ...;
hook.run( result );
return result;
}
....
}
The hook.run() method may throwRunTimeException("validation exception") there, i expect to catch the
exception at client side in onFailure(), however, i did catch the exception, but the message is "Internal Server Error",
not the exception i thrown in hook.run():"validation exception".
Any ideas to let client catch the exception i throw at server side?
Updation:
As Thomas said it's weird that validating objects that come fresh from data store, but i encounter a
situation that i don't know how to use service method:
At client i get EntityProxyId object, through the factory.find( proxyId ).fire(...) i can get the entity
from datastore, but the entity may not suitable for the user to access, in this situation i need to check it at server side, but i can't find a suitable place to do the
validation, Any ideas about this?
RequestFactory doesn't expect exceptions to be thrown by locators. Exceptions should only be thrown by service methods, and will be directed to the appropriate Receiver on the client-side (the one attached to the service method that threw).
Outside service methods, the only exceptions that gets routed to the client are ReportableExceptions, that can only be thrown from a ServiceLocatorDecorator's report() methods. That means you could hook your own ServiceLocatorDecorator that catches exceptions from your locators and report()s them.
That said, validating objects that come fresh from your data store seems weird. You might want to provide a ServiceLocatorDecorator that overrides validate() (that'll validate the objects after the changes coming from the client have been applied). The errors will go back to the client in the Receiver's onConstraintViolations, and the RequestContext will be unfrozen so you can further edit your proxies and fire() again.