Validation with Hibernate validator returns 400 instead of 422

1,279 views
Skip to first unread message

Dev

unread,
Jul 15, 2015, 11:09:40 AM7/15/15
to dropwiz...@googlegroups.com


public Class  Offer {


   
String name;
   
 
@NotNull
   
String id;


   
Date start;


 
Date end;


//getters and setters
}



and a resource class method like this



@POST
@Consumes(APPLICATION_JSON)
@Produces({APPLICATION_JSON})
@Path("/offers")
public Response createOffer(@Valid Offer offer) {
 
//blah
}




My input is something like this:

{
  "id" : "someId"
  
}

This gives a http 400 Bad request back from Jetty, whereas I expect something like 422 Unprocessable entity - name cannot be null


What could I be doing wrong?

On Dropwizard 0.8.1

Ian Barfield

unread,
Jul 15, 2015, 12:30:32 PM7/15/15
to dropwiz...@googlegroups.com

It looks like only id is annotated with not null, and your request has that set. Why would it complain about name?


--
You received this message because you are subscribed to the Google Groups "dropwizard-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dropwizard-us...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

babc...@umich.edu

unread,
Jul 15, 2015, 12:57:59 PM7/15/15
to dropwiz...@googlegroups.com
Maybe a more complete example will help. I put together a quick example and it worked. This is how my class looked.

public class Offer {
 
@JsonProperty
 
private String name;

 
@JsonProperty
 
private String id;

 
public String getName() {
   
return name;
 
}

 
public String getId() {
   
return id;
 
}
}

Dev

unread,
Jul 16, 2015, 2:30:50 AM7/16/15
to dropwiz...@googlegroups.com
Yes you're right I should have had a more complete example. Here it is now!

The javabean

public class Offer {

 
private String id;

 
@NotEmpty
 
private String name;

 
@JsonSerialize(using = APIDateFormatSerializer.class)
 
private ZonedDateTime start;

 
@JsonSerialize(using = APIDateFormatSerializer.class)
 
private ZonedDateTime end;

 
private boolean enabled;


 
public String getId() {
 
return id;
 
}


 
public void setId(String id) {
 
this.id = id;

 
}

 
public String getName() {
 
return name;
 
}


 
public void setName(String name) {
 
this.name = name;
 
}

 
public ZonedDateTime getStart() {
 
return start;
 
}

 
public void setStart(ZonedDateTime start) {
 
this.start = start;
 
}

 
public ZonedDateTime getEnd() {
 
return end;
 
}

 
public void setEnd(ZonedDateTime end) {
 
this.end = end;
 
}

 
public boolean isEnabled() {
 
return enabled;
 
}

 
public void setEnabled(boolean enabled) {
 
this.enabled = enabled;
 
}

 
public URI createLocation() {
 
return URI.create("/campaigns/" + getId());
 
}
}


The resource class



@Path("/v2")
public class OfferResource {


 
@POST
 
@Consumes(APPLICATION_JSON)
 
@Produces({APPLICATION_JSON})
 
@Path("/offers")

 
public Response createOffer(@Valid Offer campaign) {
 
return created(aDefaultOffer().createLocation())
 
.build();
 
}

------

JSON in the post

{"id":"123",
 "name": ""}







A constraintviolationException is indeed thrown from the code but it's not mapped to the correct status code.

I did some more debugging on this. One of the things I found was that I had a jar <groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId> in my classpath. This I suspected led to a different path in the code. I have removed that.

Now instead of a 400 I start seeing an error 500 being thrown

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>Error 500 Request failed.</title>
    </head>
    <body>
        <h2>HTTP ERROR 500</h2>
        <p>Problem accessing /v2/offers. Reason:

            <pre>    Request failed.</pre>
        </p>
        <hr>
            <i>
                <small>Powered by Jetty://</small>
            </i>
            <hr/>
        </body>
    </html>

Part of the stack trace

WARN [2015-07-16 06:22:59,008] org.eclipse.jetty.server.HttpChannel: /v2/campaigns ! javax.validation.ConstraintViolationException: The request entity had the following errors: ! at io.dropwizard.jersey.jackson.JacksonMessageBodyProvider.validate(JacksonMessageBodyProvider.java:96) ~[dropwizard-jersey-0.8.1.jar:0.8.1] ! at io.dropwizard.jersey.jackson.JacksonMessageBodyProvider.readFrom(JacksonMessageBodyProvider.java:60) ~[dropwizard-jersey-0.8.1.jar:0.8.1] !

On debugging I find that in the

.m2/repository/org/glassfish/jersey/core/jersey-server/2.17/jersey-server-2.17-sources.jar!/org/glassfish/jersey/server/ServerRuntime.java:567

final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
final ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable);


I see that no exceptionmapper is found for the throwable (ConstaraintViolationException). I wonder if this could have something to do with 
the issue.

Hope this provides more insight.

/Dev.

Dev

unread,
Jul 16, 2015, 5:23:40 AM7/16/15
to dropwiz...@googlegroups.com
Okay, I managed to solve this by 

Adding environment.jersey().register(ConstraintViolationExceptionMapper.class). Then it started working.

But this should have been the default/ added by dropwizard here AbstractServerFactory.java :479
if (registerDefaultExceptionMappers == null || registerDefaultExceptionMappers) {
jersey.register(new LoggingExceptionMapper<Throwable>() {
});
jersey.register(new ConstraintViolationExceptionMapper());
jersey.register(new JsonProcessingExceptionMapper());
jersey.register(new EarlyEofExceptionMapper());
}

After more debugging I eventually found that I had copied the .yaml file from some other project where it looked like this

server:
 applicationConnectors
:
 
- type: http
 port
: 9000
 adminConnectors
:
 
- type: http
 port
: 9001
 registerDefaultExceptionMappers
: false   <------Aaaaaargggghhhhh

logging
:
 level
: TRACE

Removing the line of course made it work as it should have.

Ooooh...the perils of copy paste.

Thanks all for your answers.

...
Reply all
Reply to author
Forward
0 new messages