In restEasy I used this BadRequestExcrption to throw the error but in Spring Rest it's not working. Please specify any method to throw 400 error in Spring Rest.
Make your own BadRequestException class and throw it.
Here is an example:
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException{
public BadRequestException() {
super();
}
public BadRequestException(final String message, final Throwable cause) {
super(message, cause);
}
public BadRequestException(final String message) {
super(message);
}
public BadRequestException(final Throwable cause) {
super(cause);
}
}
You can specify your own status code in #ResponseStatus
Related
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 new to rest api.
I need to make an api that takes a string as parameter and then return boolean.
Now my question is how to i pass that string to my api, and then get the string inside my api?
Here's one example that takes a string in parameter and has a default value if the query parameter is not provided:
#Path("business/department/")
public interface DepartmentService {
#GET
#Path("/cs/availability/chat")
#Produces(MediaType.APPLICATION_JSON)
boolean getCustomerServiceAvailability(#QueryParam("type") #DefaultValue("chat") String type);
}
and the implementation class can be anything that implements your interface. In this example, it's a stateless EJB
#Stateless
public class DepartmentServiceImpl implements DepartmentService {
#Context
private HttpServletRequest request;
private static final Logger LOGGER = Logger.getLogger(DepartmentServiceImpl.class.getName());
#Override
public boolean getCustomerServiceAvailability(String scheduleType) {
RequestInfo reqInfo = new RequestInfo(request, this.getClass(), "getCustomerServiceAvailability");
boolean available;
try {
available = CallBusinessService(scheduleType);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage());
throw new ServiceException();
} finally {
reqInfo.logExecutionTime();
}
}
}
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().
How to get exception from server side, if my service method throws new MyException("Some reason");
I would like to get MyException in onFailure method, but actually I got StatusCodeException. Can I get MyException in order to show error msg on UI :
Window.alert(exception.getMessage()); -> prints : "Some reason"
#RemoteServiceRelativePath("reportService.rpc")
public interface ReportService extends RemoteService {
void saveReport(ReportDTO reportDTO) throws MyException;
}
#Override
public void saveReport(ReportDTO reportDTO) throws MyException {
//Report report = ReportFunc.INST.apply(reportDTO);
//reportRepository.save(report);
throw new MyException("Some reason");
}
reportServiceAsync.saveReport(reportDTO, new AsyncCallback<Void>() {
#Override
public void onSuccess(Void result) {
Window.alert("Successfully saved");
}
#Override
public void onFailure(Throwable e) {
Window.alert(e.getMessage());
}
});
class MyException extends RuntimeException implements Serializable{
....
}
Your exception should extend Exception, not RuntimeException.
I have a problem with #IocProvider (), annotation does not work.
The code is very similar to https://docs.jboss.org/author/display/ERRAI/Container+Wiring
public interface Test {
String getGreeting();
}
#ApplicationScoped
public class TestImpl implements Test {
public String getGreeting() {
return "Hello:)";
}
}
#IOCProvider
#Singleton
public class TestProvider implements Provider<Test> {
#Override
public Test get() {
return new TestImpl();
}
}
Then I want use DI in my broadcast service (errai-bus).
#Service
public class BroadcastService implements MessageCallback {
#Inject
Test test;
#Inject
MessageBus bus;
#Inject
public BroadcastService(MessageBus bus) {
this.bus = bus;
}
public void callback(Message message) {
MessageBuilder.createMessage()
.toSubject("BroadcastReceiver")
.with("BroadcastText", test.getGreeting()).errorsHandledBy(new ErrorCallback() {
#Override
public boolean error(Message message, Throwable throwable) {
return true;
}
}).sendNowWith(bus);
}
}
I get a error:
1) No implementation for com.gwtplatform.samples.basic.server.Test was bound.
while locating com.gwtplatform.samples.basic.server.Test
for field at com.gwtplatform.samples.basic.server.BroadcastService.test(BroadcastService.java:32)
at org.jboss.errai.bus.server.service.ServiceProcessor$1.configure(ServiceProcessor.java:118)
If I change the code to
#Inject
TestImpl test;
It works, but I need the provider. Do you have some idea?
Because you're trying to use #IOCProvider in server-side code. Errai IOC is completely client-side.