Custom Message when unknown field when quarkus-resteasy-jackson mapping - jackson

I want to set custom error Message for Unrecognized field and others errors with request body.
I tried ExceptionMapper but it dont work :/
Using quarkus and Jackson
public class ReqDTO {
private String a;
}
when send request:
{
"a": "a"
}
its look good. But when send:
{
"a": "a",
"b": "b"
}
have error response:
Unrecognized field "b"; (class ReqDTO), not marked as ignorable
Want customize this message and other bad json body (like too much fields) to own message like "BAD_JSON_SCHEMA".
Tried
#Provider
public class ExHandler implements ExceptionMapper<JsonMappingException>{
public Respone toResponse(JsonMappingException e) {
//response bad field impl
}
}
But its not work. Looks like a json handle exception faster. Tried with "Exception", "JsonParseException" but nothing changed :/
#Path("/")
public class Controller {
#Post
#Consume(APPLICATION_JSON)
#Produces(TEXT_PLAIN)
public Response getA(#Valid ReqDTO reqDTO){
//logic
}
}
#Edit
Found something like DeserializationProblemHandler but dont know how change message for handleUnknownProperty :/

#Singleton
RegisterCustomModuleCustomizer implements ObjectMapperCustomizer {
public void customize(ObjectMapper mapper){
mapper.addHandler(new DeserializationProblemHandler(){
#SneakyThrows
#Override
public boolean handleUnknownProperty(...... params){
throw new ApplicationException("ERRO_BODY_MESS");
}
}
}
}
#Provider
public class ExHandler implements ExceptionMapper<ApplicationException>{
public Respone toResponse(ApplicationException ex) {
return Response.status(BAD_REQUEST).entity(ex.getMessage()).build();
}
}

Related

Jersey ignores ExceptionMapper

I made an ExceptionMapper to catch and log all exceptions, like:
#Provider
public class CatchAllExceptionsMapper implements ExceptionMapper<Throwable> {
private static final Logger LOG = LoggerFactory.getLogger(CatchAllExceptionsMapper.class);
#Override
public Response toResponse(Throwable exception) {
LOG.error("Exception not catched!", exception);
return Response.serverError().build();
}
}
It catches the Exceptions my code throws, but if I send a Request with a JSON value that throws an IllegalStateException at my object's creation, this ExceptionMapper is ignored and I get a 400 Bad Request Response.
Funny thing is this Response is not the traditional Tomcat HTML formatted Response, its just plain text. It say just:
Cannot construct instance of `com.example.vo.AutoValue_Customer$Builder`, problem: First name is null or empty. at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 14, column: 1]
I thought this might be something short-circuiting Jersey, but my #PreMatching ContainerRequestFilter is executed beforehand, so I really have no idea why the 400 Response is not the traditional HTML one from Tomcat.
Why is this happening? What can I do to catch this and return my own Response?
As stated by Paul Samsotha in the comments, JacksonFeature from the jersey-media-json-jackson package define some ExceptionMappers, like JsonMappingException and JsonParseException. The solution is to create our own, register them within the ResourceConfig and register JacksonFeature last, otherwise it won't work.
e.g.
#Provider
#Priority(1) // hack for overriding other implementations.
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
#Provider
#Priority(1) // hack for overriding other implementations.
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
#Override
public Response toResponse(JsonParseException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(CatchAllExceptionsMapper.class);
register(JsonMappingExceptionMapper.class);
register(JsonParseExceptionMapper.class);
register(JacksonFeature.class);
}
}

UNWRAP_ROOT_VALUE gives an unexpected match

I'm pretty new to Jackson so apologies in advance if there's an obvious solution.
I'm writing a Jersey client and I'm trying to deserialize JSON in the following format:
{
"success": true,
"someEntity": {
"someField": 123
}
}
Now if the 'success' field didn't exist this would be pretty simple as I could just configure the ObjectMapper to use DeserializationFeature.UNWRAP_ROOT_VALUE and it'd deserialize the contents to SomeEntity.
My Response object looks like:
public class Response<T>
{
private boolean success;
private T entity;
public T getEntity()
{
return entity;
}
public boolean isSuccess() {
return success;
}
}
Where entity would be...
public class SomeEntity
{
private int someField;
public int getSomeField()
{
return someField;
}
}
When trying to deserialize the above JSON to Response<SomeEntity> without any configuration I get: Unrecognized field "someEntity" (class Response)
I had some luck with adding #JsonProperty("someEntity") to the entity field but obviously I'd like this response class to be generic and not have to make one for every entity passed to the client.
I'm sure there's a simple solution that I'm not seeing - thanks in advance.
Edit: I've also tried using #JsonTypeInfo to no avail.

Should an API controller attempt to catch all 500 internal server errors?

I apologize if this has been asked before, please link me to it. I am having a hard time finding discussion as to whether it is an acceptable practice to catch all internal server errors (500) in an API.
I have seen some arguments for it and some against it.
I've always heard that one should send a status instead of errors when possible. However, I do see how the 500 is semantically taking responsibility for a failed request and letting a client know that their request may not be at fault. But a status can convey that as well and then the 500 is just reserved for application container level errors not errors in the database or a class library.
Is there an accepted standard or is this an opinion topic?
e.g.
public HttpResponseMessage GetUserRoles()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, AuthorizationService.GetUserRoles());
}
catch (SqlException sqle)
{
// log the exception
return Request.CreateResponse(HttpStatusCode.BadRequest, "A data error occured. Contact IT Support.");
}
catch (Exception e)
{
// log the exception
return Request.CreateResponse(HttpStatusCode.BadRequest, "An error occured. Contact IT Support.");
}
}
A good practice is to always return the same structure
public interface IResponse<T>
{
MsgType MsgType { get; }
string Msg { get; }
T Result { get; }
string Origin { get; }
}
Then you can have a MasterApiController with these methods
[NonAction]
public IHttpActionResult ResponseOk<T>(T result)
{
return Ok<IResponse>(ResponseFactory.Create<T>(result, "", "Negocio"));
}
[NonAction]
public IHttpActionResult ResponseEx(Exception ex, string msg = "Un error ha ocurrido.")
{
return ResponseMessage(Request.CreateResponse<IResponse>(HttpStatusCode.InternalServerError, ResponseFactory.Create(msg, ex, "Negocio")));
}
And in your controller that inherits from the master one you call this
[HttpGet]
[Route("Api/Alumno/Search")]
public dynamic Search(string codigo, string nombre, string estado, int? curso, int? paralelo)
{
return ResponseOk<dynamic>(result);
}
For all your non controlled exceptions you can have an action filter and manage them
public class ErrorLoggingFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//Exception
}
}

camel custom marshalling with dataFormat name in header

I'm having two routes in two separated projects :
First route is setting the header with a data format bean name as a constant :
setHeader("dataFormatBeanName", constant("myFirstList"))
First route :
public class MyTest {
#Configuration
public static class MyTestConfig extends CamelConfiguration {
#Bean(name = "myFirstList")
public DataFormat getMyFirstListDataFormat() {
return new MyFirstListDataFormat();
}
#Bean(name = "mySecondList")
public DataFormat getMySecondListDataFormat() {
return new MySecondListDataFormat();
}
#Bean
public RouteBuilder route() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:testFirstDataFormat").setHeader("dataFormatBeanName", constant("myFirstList")).to("direct:myRoute");
from("direct:testSecondDataFormat").setHeader("dataFormatBeanName", constant("mySecondList")).to("direct:myRoute");
}
};
}
}
}
Second route is supposed to retrieve the bean name from the header and use it as a custom marshaller. Something like :
custom(header("dataFormatBeanName"))
(doesn't compile)
Anyone knows how I'm supposed to get my bean name from the header to use it in the custom method ?
#Component
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal().custom(??????????).to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
After a few more hours searching, here is the solution a found :
No changes in the first class.
Second class uses an anonymous DataFormat in which I retrieve the bean name from the header and get the spring bean from camel context before calling its marshal method.
The AbstractXxxDataFormat class belongs to project2 and is inherited by the Project1 DataFormat.
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal(new DataFormat() {
#Override
public void marshal(final Exchange exchange, final Object graph, final OutputStream stream) throws Exception {
AbstractXxxDataFormat myDataFormat = (AbstractGoalDataFormat) getContext().getRegistry().lookupByName(exchange.getIn().getHeader("dataFormatBeanName", String.class));
myDataFormat.marshal(exchange, graph, stream);
}
#Override
public Object unmarshal(final Exchange exchange, final InputStream stream) throws Exception {
return null;
}
});
routedefinition.to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
If there's any better solution available, I'll be interested.
Have you tried simple("${header.dataFormatBeanName}") to access the header?
Also, rather than passing the format bean name in a header in the first place, why not factor out each .marshal() call into two subroutes (one for formatBeanA and one for formatBeanB) and then call the appropriate subroute rather than setting the header in the first place? I believe this could be a cleaner approach.
If you really need to get it in the route as a variable (as opposed to a predicate to be used in the builder api) you could use an inline processor to extract it:
public class MyRouteBuilder extends RouteBuilder {
public void configure() throws Exception {
from("someEndpoint")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String beanName = exchange.getHeader("beanNameHeader");
}
});
}
}
Just be careful of scope and concurrency when storing the extracted beanName however.
A collegue of mine (thanks to him) found the definite solution :
set bean name in the exchange properties :
exchange.setProperty("myDataFormat", "myDataFormatAutowiredBean");
retrieve the dataFormat bean with RecipientList pattern and (un)marshal :
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:marshal"));
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:unmarshal"));
Very concise and works just fine.

GWT with Serialization

This is my client side code to get the string "get-image-data" through RPC calls and getting byte[] from the server.
CommandMessage msg = new CommandMessage(itemId, "get-image-data");
cmain.ivClient.execute(msg, new AsyncCallback<ResponseMessage>() {
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(ResponseMessage result) {
if (result.result) {
result.data is byte[].
}
}
});
From the server side I got the length of the data is 241336.
But I could not get the value in onSuccess method. It is always goes to onFailure method.
And I got log on Apache:
com.google.gwt.user.client.rpc.SerializationException: Type '[B' was
not included in the set of types which can be serialized by this
SerializationPolicy or its Class object could not be loaded.
How can I do serialisation in GWT?
1) Create a pojo which implements Serializable interface
Let this pojo has all the data you want in the response of RPC service, in this case image-data
2) Pass this pojo in the response for your RPC service.
The below tutorial has enough information for creating RPC service
http://www.gwtproject.org/doc/latest/tutorial/RPC.html
The objects you transfer to and from the server has to implement IsSerializable.
All your custom Objects within the Object you are transferring also needs to implement IsSerializable.
Your objects cannot have final fields and needs an no argument constructor.
You need getters and setters.
A common serialize object in GWT:
public class MyClass implements IsSerializable {
private String txt;
private MyOtherClass myOtherClass; // Also implements IsSerializable
public MyClass() {
}
public String getTxt() {
return this.txt;
}
public void setTxt(String txt) {
return this.txt = txt;
}
public String getMyOtherClass() {
return this.myOtherClass;
}
public void setMyOtherClass(MyOtherClass myOtherClass) {
return this.myOtherClass = myOtherClass;
}
}