In a spring data rest project i use a custom RuntimeException to be called in a custom Deserializer
public class LocalDateDeserializer extends StdDeserializer<LocalDate> {
...
#Override
public LocalDate deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException, JsonProcessingException {
String date = jsonparser.getText();
String name = jsonparser.getCurrentName();
try {
return LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE);
} catch (DateTimeParseException e) {
throw new ApiJacksonException("error on: " + name);
}
}
}
My User.class
#Data
#NoArgsConstructor
public class User extends Auditing implements Serializable {
private static final long serialVersionUID = 1L;
...
#DateTimeFormat(iso = ISO.DATE)
#JsonFormat(pattern = "yyyy-MM-dd")
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDate birthdate;
}
When i send a POST request with a wrong date format the #ControllerAdvice catch the custom RuntimeException
But when i send a PATCH request with a wrong date format it seams that the RuntimeException is wrapped by the JsonMappingException and can't be catched by the #ControllerAdvice
in the properties file i have set
spring.jackson.deserialization.wrap-exceptions = false
Have i missed some thing!
Resolved, indeed an update request (patch/put) with an invalid Date format will fire a HttpMessageNotReadableException that wraps the custom RuntimeException, in #ControllerAdivce we have to override handleHttpMessageNotReadable
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
if(ex.getCause() instanceof ApiJacksonException) {
// execute custom code...
}
return super.handleHttpMessageNotReadable(ex, headers, status, request);
}
Related
I am trying to test this filter:
public class HttpMethodOverrideHeaderFilter extends OncePerRequestFilter {
private static final String X_HTTP_METHOD_OVERRIDE_HEADER = "X-HTTP-Method-Override";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (isMethodOverriden(request)) {
HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, request.getHeader(X_HTTP_METHOD_OVERRIDE_HEADER).toUpperCase(Locale.ENGLISH));
filterChain.doFilter(wrapper, response);
}
else {
filterChain.doFilter(request, response);
}
}
private boolean isMethodOverriden(HttpServletRequest request) {
String methodOverride = request.getHeader(X_HTTP_METHOD_OVERRIDE_HEADER);
return RequestMethod.POST.name().equalsIgnoreCase(request.getMethod()) &&
(RequestMethod.PUT.name().equalsIgnoreCase(methodOverride) || RequestMethod.DELETE.name().equalsIgnoreCase(methodOverride));
}
protected static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
#Override
public String getMethod() {
return this.method;
}
}
}
And this is the unit test:
#RunWith(MockitoJUnitRunner.class)
public class HttpMethodOverrideHeaderFilterTest {
private static final String X_HTTP_METHOD_OVERRIDE_HEADER = "X-HTTP-Method-Override";
private HttpMethodOverrideHeaderFilter httpMethodOverrideHeaderFilter;
#Mock
private HttpServletRequest httpServletRequest;
#Mock
private HttpServletResponse httpServletResponse;
#Mock
private FilterChain filterChain;
#Before
public void setUp() {
httpMethodOverrideHeaderFilter = new HttpMethodOverrideHeaderFilter();
}
#Test
public void testDoFilterInternalWithPUTMethodAsOverrideHeader() throws Exception {
when(httpServletRequest.getHeader(X_HTTP_METHOD_OVERRIDE_HEADER)).thenReturn("PUT");
when(httpServletRequest.getMethod()).thenReturn("POST");
HttpServletRequest wrapper = new HttpMethodOverrideHeaderFilter.HttpMethodRequestWrapper(httpServletRequest, "PUT");
httpMethodOverrideHeaderFilter.doFilterInternal(httpServletRequest, httpServletResponse, filterChain);
verify(filterChain).doFilter(wrapper, httpServletResponse);
}
}
The test is not passing as wrapper is not the same instance. Basically what I need to know is if the wrapper was set the PUT method. Any ideas?
I found a way to do it:
#Test
public void testDoFilterInternalWithPUTMethodAsOverrideHeader() throws Exception {
when(httpServletRequest.getHeader(X_HTTP_METHOD_OVERRIDE_HEADER)).thenReturn("PUT");
when(httpServletRequest.getMethod()).thenReturn("POST");
httpMethodOverrideHeaderFilter.doFilterInternal(httpServletRequest, httpServletResponse, filterChain);
ArgumentCaptor<ServletRequest> requestCaptor = ArgumentCaptor.forClass(ServletRequest.class);
ArgumentCaptor<ServletResponse> responseCaptor = ArgumentCaptor.forClass(ServletResponse.class);
verify(filterChain).doFilter(requestCaptor.capture(), responseCaptor.capture());
HttpMethodOverrideHeaderFilter.HttpMethodRequestWrapper wrapper = (HttpMethodOverrideHeaderFilter.HttpMethodRequestWrapper) requestCaptor.getValue();
assertEquals(wrapper.getMethod(), "PUT");
}
if anyone know any better way, let me know!!!
I have a REST handler servlet defined as follows (this works perfectly):
//REST handler context
ServletContextHandler restHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
restHandler.setContextPath("/");
// Jersey REST handling servlet
ServletHolder jerseyServlet = restHandler.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tell Jersey which REST service class to load....
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", RestHandler.class.getCanonicalName());
I now want to add a authentication filter, which I do as:
FilterHolder authFilter = restHandler.addFilter(AuthFilter.class, "/",
EnumSet.of( DispatcherType.ASYNC,
DispatcherType.ERROR,
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.REQUEST));
if (authFilter == null) {
dlog.debug("Failed to load authentication filter");
};
All good so far, however, the filter does not fire on incoming REST. Calls still go through. The AuthFilter is straight from sample code:
public class AuthFilter implements javax.servlet.Filter {
private static final Logger dlog = Dlog.get();
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter)
throws IOException, ServletException {
dlog.entry(request, response, filter);
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest.getHeader(AUTHENTICATION_HEADER);
AuthService authenticationService = new AuthService();
boolean authenticationStatus = authenticationService.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
dlog.exit();
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
I use handler collection as I also have a resource handler to serve static web pages besides the REST calls.
HandlerCollection handlerList = new HandlerCollection();
handlerList.setHandlers(new Handler[] { resourceHandler,
restHandler,
new DefaultHandler(),
requestLogHandler });
What else I need to do? I have scanned through number of related posts and come up empty. Thanks in advance.
How can I test one PUT request with Spring Boot??
I have this method:
#RequestMapping(method = RequestMethod.PUT, value = "/")
public NaturezaTitulo save(#RequestBody NaturezaTitulo naturezaTitulo){
return naturezaTituloService.save(naturezaTitulo);
}
and this test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class NaturezaTituloControllerTest {
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
private MockMvc mockMvc;
private HttpMessageConverter mappingJackson2HttpMessageConverter;
private List<NaturezaTitulo> naturezaTituloList = new ArrayList<>();
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream().filter(
hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get();
Assert.assertNotNull("the JSON message converter must not be null",
this.mappingJackson2HttpMessageConverter);
}
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void naturezaTituloNotFound() throws Exception {
mockMvc.perform(get("/naturezatitulo/55ce2dd6222e629f4b8d6fe0"))
.andExpect(status().is4xxClientError());
}
#Test
public void naturezaTituloSave() throws Exception {
NaturezaTitulo naturezaTitulo = new NaturezaTitulo();
naturezaTitulo.setNatureza("Testando");
mockMvc.perform(put("/naturezatitulo/").content(this.json(naturezaTitulo))
.contentType(contentType))
.andExpect(jsonPath("$.id", notNullValue()));
}
protected String json(Object o) throws IOException {
MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
this.mappingJackson2HttpMessageConverter.write(
o, MediaType.APPLICATION_JSON, mockHttpOutputMessage);
return mockHttpOutputMessage.getBodyAsString();
}
}
but I got this error:
java.lang.IllegalArgumentException: json can not be null or empty at
com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:259)
how can I pass one object from body in Put test?
tks
I am trying to mock static method using powermock.
Below is my code:
public class Helper{
public static User getLoggedInUser(HttpServletRequest request) throws NotFoundException {
String access = request.getHeader("Authorization");
if(access == null || access.isEmpty()) {
throw new Exception("Access is null");
}
User user = new User();
return user;
}
}
And this is the controller function from where i am calling the static method getUser:
#RequestMapping(value = "user/userInfo/{Id}", method = RequestMethod.GET, headers = "Accept=application/json")
public #ResponseBody
ResultDTO getUser(#PathVariable("Id") Integer Id, HttpServletRequest request) throws NotFoundException, UnauthorizedException {
Integer userID = -1;
User user = Helper.getLoggedInUser(request);
if(user != null){
userID = user.getUserId();
}
//do something
}
And this is my test class:
//#RunWith(PowerMockRunner.class)
//#PrepareForTest(Helper.class)
public class CustomerControllerNGTest {
#InjectMocks
private userController instance = new PaymentCustomerController();
public PaymentCustomerControllerNGTest() {
}
#BeforeClass
public void setUpClass() throws Exception {
}
#AfterClass
public static void tearDownClass() throws Exception {
}
#BeforeMethod
public void setUpMethod() throws Exception {
try{
MockitoAnnotations.initMocks(this);
}catch(Exception ex){
System.out.println(ex.getMessage());
}
try{
mockMvc = MockMvcBuilders.standaloneSetup(instance).build();
// mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}catch(Exception ex){
System.out.println(ex.getMessage());
}
}
#AfterMethod
public void tearDownMethod() throws Exception {
}
#Test
public void testGetUserInfo() throws Exception {
User user = new User();
user.setUserId(1234);
HttpServletRequest request = mock(HttpServletRequest.class);
//this is for the static method
PowerMockito.mockStatic(Helper.class);
**PowerMockito.when(Helper.getLoggedInUser(request)).thenReturn(user);**
//do something
}
}
Now whenever i am executing the test case, and whenever it is executing the lone marked with bold, it is going inside the static method and throwing the exception "Access is null" rather than mocking the method , it is executing the method. Any idea?
I also tried by uncommenting these lines:
//#RunWith(PowerMockRunner.class)
//#PrepareForTest(Helper.class)
but still same exception.
Thanks
Try to uncomment:
//#RunWith(PowerMockRunner.class)
//#PrepareForTest(Helper.class)
and use
Mockito.when(Helper.getLoggedInUser(request)).thenReturn(user);
I wrote blog post on topic, that contain links to working examples on GitHub. These use TestNg instead of JUnit, but this shouldn't matter.
EDIT
I would suggest to always use latest combination of Mockito and PowerMock available. Older combinations were often pretty buggy with confusing errors. Current latest combination is Mockito 1.9.5-rc1+, PowerMock 1.5+. Pre-1.5 versions of PowerMock wasn't Java7 compliant.
I'm using Apache-cxf to implement Restful web services. I'm using ExceptionMapper to build the response object in case if exception occurs. I'm getting below error if any exception occurs.
"No message body writer has been found for response class MyException."
I could find some of the post which suggest to custom Writer which implements MessageBodyWriter, but i'm not very clear why do i need a custom writer if entity object (ErrorInfo) which is passed for building response is the jaxb object. This might be a very silly question but just want to understand.
#Provider
public class MyExceptionMapper implements
ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException ex) {
Response.Status statusCode = exceptionMap.get(ex.getClass());
ErrorInfo errorInfo=new ErrorInfo();
errorInfo.setErrorCode(ex.getErrorCode());
errorInfo.setErrorMessage(ex.getMessage());
return Response.status(statusCode).entity(ex).build();
}
}
#XmlRootElement(name = "errorInfo")
#XmlType(propOrder = { "errorCode", "errorMessage"})
public class ErrorInfo {
private String errorCode;
private String errorMessage;
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
Had the same problem, for me setting the content type explicitly solved the issue:
return Response.status(statusCode).entity(ex).type(MediaType.APPLICATION_JSON).build();