Following the documentation http://docs.spring.io/spring-data/rest/docs/2.4.2.RELEASE/reference/html/#validation I set up a very simple Validator for a spring-data-rest repository invocation:
public class DealValidator implements Validator {
#Override
public boolean supports(Class<?> aClass) {
return Deal.class.isAssignableFrom(aClass);
}
#Override
public void validate(Object o, Errors errors) {
errors.reject("deal.error", "No deal");
}
}
And this is the configuration
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", new DealValidator());
}
#Configuration
static class I18nConfiguration {
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("classpath:messages");
return source;
}
}
The configuration seems to be alright, the validator is called correctly, the http-request yields an error response, but no error text is returned, neither from the messages.properties nor the default text. Is this a bug?
I came across the same issue. Only validation errors that reference a field are serialized by spring-data-rest.
So you could use rejectValue(String field, String errorCode, String defaultMessage) instead of reject
See org.springframework.data.rest.webmvc.support.RepositoryConstraintViolationExceptionMessage for implementation details. The implementation just processes org.springframework.validation.Errors#getFieldErrors().
Related
I have exposed as service as below
restConfiguration().component("servlet").bindingMode(RestBindingMode.json);
rest("/batchFile").consumes("application/json").post("/routeStart").type(BatchFileRouteConfig.class).to("startRouteProcessor");
Based upon the request from rest service,i would start camel route in processor as below
#Component("startRouteProcessor")
public class StartRouteProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
BatchFileRouteConfig config = exchange.getIn().getBody(BatchFileRouteConfig.class);
String routeId = config.getRouteId();
String sourceLocation = config.getSourceLocation();
exchange.getContext().startRoute(routeId);
}
}
I need to pass the sourceLocation from above bean to below route
#Component
public class FileReaderRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file:sourceLocation")
.log("File Reader Route route started");
}
}
Above is sample code..request you to help me in passing the sourcelocation from StartRouteProcessor to FileReaderRoute
This is not possible, since in your example is FileReaderRoute already started at the time of calling batchFile endpoint.
You can do it in slightly different way.
Extract your FileReaderRoute to direct. Something like:
#Component
public class FileReaderRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:fileReaderCommon")
.log("File Reader Route route started");
}
}
And then you can create new route at runtime:
#Component("startRouteProcessor")
public class StartRouteProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
BatchFileRouteConfig config = exchange.getIn().getBody(BatchFileRouteConfig.class);
exchange.getContext().addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("file:"+config.getSourceLocation())
.routeId(config.getRouteId())
.to("direct:fileReaderCommon");
}
});
}
}
Do not forget to take sufficient sanitizing of input, since you are allowing user to create file consumer based on user input. In your approach, there is a high risk of path traversal attack.
Before marking this as a duplicate: I read here and there that an ExceptionMapper will solve my problem, but for some reason it does not catch the ConstraintViolationException.
Update
The problem is solved: Using a separate, more specific ExceptionMapper works (one that implements ExceptionMapper< ConstraintViolationException >). But I don't fully understand why a more general exception mapper (one that implements ExceptionMapper< Exception >) does NOT catch my ConstraintViolationException.
Original question:
I am introducing bean validation to my REST Methods:
#PUT
public Response updateUser(#NotNull #Valid UserUpdateDTO userUpdateDTO) {
return ResponseUtil.ok(userService.updateUser(userUpdateDTO));
}
When a validation fails, I get a 400 response:
[PARAMETER]
[updateUser.arg0.initials]
[Initials must be between 3 and 5]
[AD]
I would like to catch the ConstraintViolationException before the response is sent because I have my own ResponseFactory.
Here is my ExceptionMapper (that works with my other exceptions!)
#Provider
public class ApiExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception e) {
Throwable cause = (e instanceof EJBException) && e.getCause() != null ? e.getCause() : e;
if (cause instanceof BadRequestException) {
logger.error("BadRequest", cause);
return ResponseUtil.badRequest(cause.getMessage());
} else if (cause instanceof ForbiddenException) {
logger.error("Forbidden", cause);
return ResponseUtil.forbidden(cause.getMessage());
} else if (cause instanceof ServerException) {
logger.error("ServerException", cause);
return ResponseUtil.serverError(cause.getMessage());
} else if (cause instanceof ConstraintViolationException) {
return ResponseUtil.badRequest("Validation failed");
}
// Default
logger.error("unexpected exception while processing request", cause);
return ResponseUtil.serverError(cause);
}
}
The ExceptionMapper is not even called when a validation problem occurs, and I get the default 400 error right away.
What am I doing wrong ? I suspect that it has something to do with the fact that the exception is not thrown within the method's body, but rather in its signature.
I am using Wildfly 11 RC and its default validation
Given a Rest Service such as:
#Stateless
#Path("/people")
public class PersonService {
#PersistenceContext(name = "people")
private EntityManager em;
#POST
#Path("/")
#Consumes(APPLICATION_JSON)
public Response create(#Valid Person person) throws DuplicateKeyException {
em.persist(person);
return Response.created(UriBuilder.fromResource(PersonService.class)
.path(PersonService.class, "getPerson")
.resolveTemplate("id", person.getId()).build())
.build();
}
}
then the following ExceptionMapper works just fine by itself:
#Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException>{
#Inject
private Logger logger;
private static class ConstraintViolationBean {
private final String propertyName;
private final String message;
private final String invalidValue;
private ConstraintViolationBean(ConstraintViolation constraintViolation) {
final StringBuilder propertyPath = new StringBuilder();
for (Path.Node node: constraintViolation.getPropertyPath()) {
if (propertyPath.length() > 0) {
propertyPath.append('.');
}
propertyPath.append(node.getName());
}
this.propertyName = propertyPath.toString();
this.message = constraintViolation.getMessage();
this.invalidValue = constraintViolation.getInvalidValue().toString();
}
public String getPropertyName() {
return propertyName;
}
public String getMessage() {
return message;
}
public String getInvalidValue() {
return invalidValue;
}
}
#Override
public Response toResponse(ConstraintViolationException exception) {
logger.log(Level.WARNING, "Constraint violation: {}", exception.getMessage());
List<ConstraintViolationBean> messages = new ArrayList<>();
for (ConstraintViolation cv : exception.getConstraintViolations()) {
messages.add(new ConstraintViolationBean(cv));
}
return Response.status(Response.Status.BAD_REQUEST)
.entity(messages)
.build();
}
}
This is real working (not production) code that I have been messing with for fun. There is also an ExceptionMapper for the DuplicateKeyException.
You can find the source on github at jaxrs-people, which is essentially an experiment.
One thing I have noticed is that EJBExceptions seem to be unwrapped before the ExceptionMapper is selected and invoked.
Update:
Now, if I add the following implementation of ExceptionMapper<Exception> to the deployment, then this one is invoked and the remaining exception mappers are ignored.
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception exception) {
return Response.status(Response.Status.NOT_ACCEPTABLE)
.build();
}
}
Therefore it seems that because your ApiExceptionMapper is actually catching everything and your other ExceptionMappers will be ignored.
It looks like you need to either implement a separate ExceptionMapper for each of BadRequestException, ForbiddenException and ServerException, or some common ancestor class that is not Exception or RuntimeException.
I think that separate implementations would be better because code without if statements is easier to unit test.
What the Spec says:
§4.4 of "JAX-RS: Java™ API for RESTful Web Services (v2.0)" contains the statement:
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
This behaviour corresponds with what we have experienced here.
I'm trying to debug an web application running on Glassfish and I want to cause the server to return a CLIENT_ERROR or SERVER_ERROR.
The returned javax.ws.rs.core.Response to the calling server should include an entity. What is the best way to do this?
Create a filter and make it return the required Response:
javax.ws.rs.container.ContainerRequestFilter
#Provider
public class RequestFilter implements ContainerRequestFilter {
/** {#inheritDoc} */
#Override
public void filter(final ContainerRequestContext req) throws IOException {
if (req.getUriInfo().getPath().toLowerCase().contains("pathToMatch")) {
final Response newResp = Response.status(500).entity("<test>test</test>").type(MediaType.valueOf(MediaType.TEXT_HTML)).build();
req.abortWith(newResp);
}
}
}
I have the problem, that two methods of my rest service bring the error on deploy, that there is no injection source.
My Service looks like this:
#Path("/chatservice")
public class ChatServiceImpl implements ChatService{
#POST
#Path("/registerToServer")
#Consumes(MediaType.APPLICATION_JSON)
#Override
public Response registerToServer(User user) {
UserList userListObject = getAllChatableUsers(user);
return Response.status(200).entity(userListObject).build();
}
#POST
#Path("/sendMessage")
#Consumes(MediaType.APPLICATION_JSON)
#Override
public Response sendMessage(Message message) {
boolean isSuccess = putMessageIntoDatabase(message);
return Response.status(200).build();
}
#POST
#Path("/getAllMessagesForUser")
#Consumes(MediaType.APPLICATION_JSON)
#Override
public Response getAllMessagesForUser(UserWithRecipient userWithRecipient) {
return Response.status(200).build();
}
#POST
#Path("/getAllMessagesForUser/{numberOfMessages}")
#Consumes(MediaType.APPLICATION_JSON)
#Override
public Response getMessagesForUser(#PathParam("numberOfMessages") int numberOfMessages, UserWithRecipient userWithRecipient) {
return Response.status(200).build();
}
The Class whith the Problem is the following:
#XmlSeeAlso(User.class)
#XmlRootElement
public class UserWithRecipient {
private User user;
private User recipient;
public UserWithRecipient() {
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public User getRecipient() {
return recipient;
}
public void setRecipient(User recipient) {
this.recipient = recipient;
}
}
And the error I get is the following:
[[FATAL] No injection source found for a parameter of type public javax.ws.rs.core.Response de.hszg.fei.ws.service.ChatServiceImpl.getMessagesForUser(int,de.hszg.fei.ws.model.UserWithRecipient) at index 0.; source='ResourceMethod{httpMethod=POST, consumedTypes=[application/json], producedTypes=[], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class de.hszg.fei.ws.service.ChatServiceImpl, handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor#6da34189]}, definitionMethod=public javax.ws.rs.core.Response de.hszg.fei.ws.service.ChatServiceImpl.getMessagesForUser(int,de.hszg.fei.ws.model.UserWithRecipient), parameters=[Parameter [type=int, source=numberOfMessages, defaultValue=null], Parameter [type=class de.hszg.fei.ws.model.UserWithRecipient, source=null, defaultValue=null]], responseType=class javax.ws.rs.core.Response}, nameBindings=[]}']]]
Can you tell me what is the problem with this class. I also don't understand, why the sendMessage() method doesn't brings the same problem.
I found out that the problem was, that I imported the wrong #PathParam Annotation.
Adding this answer to add a bit more context to the question / accepted answer.
As Daniel Müssig described in his answer, I too used the wrong #PathParam annotation (I was using the one from javax.websocket.server, but obviously it should be the one from javax.ws.rs).
Here are some more error messages that I ran into, that might hopefully help some googlers out there!
org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
[[FATAL] No injection source found for a parameter of type [... more specific method data on this line]
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:553) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:182) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:348) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:345) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.internal.Errors.process(Errors.java:315) ~[jersey-common-2.21.jar:na]
at org.glassfish.jersey.internal.Errors.process(Errors.java:297) ~[jersey-common-2.21.jar:na]
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255) ~[jersey-common-2.21.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:345) ~[jersey-server-2.21.jar:na]
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:390) ~[jersey-container-servlet-core-2.21.jar:na]
I found, i was using com.sun.jersey.multipart.FormDataParam. Right import is org.glassfish.jersey.media.multipart.FormDataParam. This fixed my issue.
Reference - https://github.com/jersey/jersey/blob/master/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MultiPartFieldInjectedResource.java
I'm new to GWT. What good solutions exist for form validation? I'd like to avoid rolling my own if possible.
In my GWT applications I always use my custom validator classes. I have created my own textbox class which extends gwt textbox. And I call CustomTextBox instead of gwt's textbox.
CustomTextBox.java
public class CustomTextBox extends TextBox implements HasValidators{
private static final String TEXTBOX_VALIDATION_ERROR_STYLE = "error-text-box";
private String errorMessage = "";
private List<Validator> validators = new ArrayList<Validator>();
public CustomTextBox() {
}
public CustomTextBox(String name) {
setName(name);
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public void addValidator(Validator validator) {
validators.add(validator);
}
public boolean validate() {
boolean validationResult = true;
for (Validator validator : validators) {
validationResult = validator.validate(getValue().trim());
if (!validationResult) {
errorMessage = validator.getErrorMessage();
break;
}
errorMessage = validator.getErrorMessage();
}
setErrorStyles(validationResult);
return validationResult;
}
private void setErrorStyles(boolean validationResult) {
if (validationResult) {
removeStyleName(TEXTBOX_VALIDATION_ERROR_STYLE);
setTitle("");
} else {
addStyleName(TEXTBOX_VALIDATION_ERROR_STYLE);
setTitle(errorMessage);
}
}
#Override
public void setValue(String s) {
removeStyleDependentName(TEXTBOX_VALIDATION_ERROR_STYLE);
super.setValue(s);
}
#Override
public String getValue() {
return super.getValue().trim();
}
}
Validator.java
public abstract class Validator {
public String errorMessage;
public abstract boolean validate(String value);
public abstract String getErrorMessage();
}
Sample Email validator
public class EmailValidator extends Validator {
public boolean validate(String value) {
if (value.matches("^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
errorMessage = "";
return true;
} else {
errorMessage = "Enter valid email Id";
return false;
}
}
public String getErrorMessage() {
return errorMessage;
}
}
My validation error looks like following
If you like this approach you can follow this. The problem here is we don't display the error message directly in the UI. Only in tooltip we are showing.
GXT and SmartGWT (both of them I'd highly advise against, for many reasons) have form validation.
GWT's Editor framework provides a mean to display javax.validation.ConstraintViolation (you'd still have to do the actual display by implementing HasEditorErrors to receive the errors for a given field; the Editor framework only dispatches the errors to the appropriate "fields") but nothing to validate on the client side.
The recently released, GWT 2.3 has preliminary support for JSR 303 Bean Validation on the client side but it's not yet finished: http://code.google.com/p/google-web-toolkit/wiki/BeanValidation
I think GWT 2.4 will have full (or almost full) support.
Note that GWT's stake on validation is on validating the objects, not about validating the "form fields" editing an object's properties.
We are currently using this project for forms validation:
http://code.google.com/p/gwt-validation/
It's the source code that serves as the basis of the new GWT 2.4 Validation framework.
Chris Buffalo has been doing an amazing work on that project. Works out of the box for us.