I have a rest controller with an endpoint for a download.
The endpoint can download a resource/entity, if it doesn't exist an error will be thrown.
The endpoint produces
application/octet-stream --> download
application/json --> error
Example
#RestController
public class ResourceController {
#GetMapping( value = "/{id}", produces = { "application/json", "application/octet-stream" } )
public ResponseEntity<Resource> download( #PathVariable final int id ) {
if ( id % 2 == 0 ) {
throw new EntityNotFoundException();
}
return ResponseEntity.ok( new ClassPathResource( "application.properties" ) );
}
}
The ExceptionHandler
#ControllerAdvice
public class ResponseExceptionHandler {
#ExceptionHandler( EntityNotFoundException.class )
public ResponseEntity<ErrorResponse> handleEntityNotFoundException( final HttpServletRequest request,
final EntityNotFoundException e ) {
return notFound( e );
}
protected ResponseEntity<ErrorResponse> notFound(
final Throwable e ) {
return new ResponseEntity<>( new ErrorResponse().error( e.getMessage() ), HttpStatus.NOT_FOUND );
}
}
If the client uses the accept header /*/ every works fine. Download works and if the entity is not existing an error is shown.
If the client uses the accept header application/octet-stream and the entity is existing the ExceptionHandler is not working cause of wrong media type and the service return a 500 error code. The exception is HttpMediaTypeNotAcceptableException.
Stacktrace:
2022-10-20 13:33:39.970 WARN 75059 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Failure in #ExceptionHandler com.example.demo.ResponseExceptionHandler#handleEntityNotFoundException(HttpServletRequest, EntityNotFoundException)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:249) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:219) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:135) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:428) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver.doResolveException(AbstractHandlerMethodExceptionResolver.java:75) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:142) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:80) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1331) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1142) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1088) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.23.jar:5.3.23]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
...
2022-10-20 13:33:39.972 ERROR 75059 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.example.demo.EntityNotFoundException: Not found] with root cause
com.example.demo.EntityNotFoundException: Not found
at com.example.demo.ResourceController.download(ResourceController.java:16) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
How can I solve this?
Another exception handler for HttpMediaTypeNotAcceptableException didn't work.
EDIT: Here is a small example
https://github.com/MelleD/wrong-media-type-exception-handler-example
Start and try: http://localhost:8080/2
with accept header "application/octet-stream")
Related
Sorry I Miss understood the problem.
The issue is ETag Header.
This is header response when i make curl request to server.
Content-Length: 228
Content-Type: application/json
X-ORACLE-DMS-ECID: 644f1021-bee9-4a11-b8ab-ab8557a746c0-00000f2c
ETag: 1
X-ORACLE-DMS-RID: 0
This is StackTrace:
java.lang.IllegalArgumentException: Invalid ETag: does not start with W/ or "
at org.springframework.util.Assert.isTrue(Assert.java:121) ~[spring-core-5.3.9.jar:5.3.9]
at org.springframework.http.HttpHeaders.setETag(HttpHeaders.java:1039) ~[spring-web-5.3.9.jar:5.3.9]
at org.springframework.integration.http.support.DefaultHttpHeaderMapper.setETag(DefaultHttpHeaderMapper.java:588) ~[spring-integration-http-5.5.2.jar:5.5.2]
at org.springframework.integration.http.support.DefaultHttpHeaderMapper.setHttpHeader(DefaultHttpHeaderMapper.java:406) ~[spring-integration-http-5.5.2.jar:5.5.2]
at org.springframework.integration.http.support.DefaultHttpHeaderMapper.fromHeaders(DefaultHttpHeaderMapper.java:377) ~[spring-integration-http-5.5.2.jar:5.5.2]
at org.springframework.integration.http.support.DefaultHttpHeaderMapper.fromHeaders(DefaultHttpHeaderMapper.java:75) ~[spring-integration-http-5.5.2.jar:5.5.2]
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.setupResponseAndConvertReply(HttpRequestHandlingEndpointSupport.java:427) ~[spring-integration-http-5.5.2.jar:5.5.2]
at org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway.handleRequest(HttpRequestHandlingMessagingGateway.java:114) ~[spring-integration-http-5.5.2.jar:5.5.2]
at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:52) ~[spring-webmvc-5.3.9.jar:5.3.9]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1064) ~[spring-webmvc-5.3.9.jar:5.3.9]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.9.jar:5.3.9]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.9.jar:5.3.9]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.9.jar:5.3.9]
This is my sample code, where service is activated when message is sent to channel 'request.input' if the remote server respond with header ETag: 1 exception is raised.
#Value("${auth.token}")
private String authToken;
#Value("${auth.key}")
private String authKey;
#Value("${url}")
private String url;
#Bean
public IntegrationFlow outRequest() {
return IntegrationFlows.from(Http.inboundGateway("/request")
.requestMapping(m-> m.methods(HttpMethod.POST))
.requestPayloadType(RequestPayloadDTO.class).errorChannel("errorChannel"))
.transform(requestTransformer)
.gateway("request.input")
.get();
}
#Bean
public IntegrationFlow request() {
return flow -> flow.enrichHeaders((headerEnricherSpec -> {
headerEnricherSpec.header(authKey,authToken,true);
})).handle(Http.outboundGateway(url)
.mappedRequestHeaders(authKey)
.httpMethod(HttpMethod.POST)
.expectedResponseType(ResponseDTO.class)
).transform(responseTransformer);
}
#MessagingGateway(defaultRequestChannel = "request.input")
public interface RequestGateway {
ResponseDTO exchange(String out);
}
I have resolved by adding
.transform(responseTransformer).headerFilter("ETag");
Thank you.
I am having troubles figuring out why I am getting this error on server:
I built a simple Controller that accepts a Request Dto:
#ResponseBody
#PostMapping("/log")
fun logReuqest(#RequestBody request: Request) {
logger.info("Request: $request")
}
The Reuest and the nested DTOs look like this:
data class Request(val customer: Customer, val dealer: Dealer)
data class Customer(val id: String, val name: String)
data class Dealer( val id: String, val name: String)
The json I am sending is:
{
"customer" : {
"id" : "123",
"name" : "customer"
},
"dealer" : {
"id" : "123",
"name" : "dealer"
}
}
The problem: I send a reuquest from postman with "application/json" contentType header, it works perfectly. But whenever I deploy this app and it gets an incoming request from another service, I get this Jackson error:
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
construct instance of `com.service.dto.Request` (although at least one Creator exists): no
String-argument constructor/factory method to deserialize from String value
What is wrong with the code, why does it work from postman and whenever I deploy it doesn't work on when getting incoming requests from another service with restTemplate?
StackTrace:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.service.dto.Request` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
"customer" : {
"id" : "123",
"name" : "customer"
},
"dealer" : {
"id" : "123",
"name" : "dealer"
}'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.service.dto.Request` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
"customer" : {
"id" : "123",
"name" : "customer"
},
"dealer" : {
"id" : "123",
"name" : "dealer"
}')
at [Source: (PushbackInputStream); line: 1, column: 1]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:387)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:186)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:158)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:131)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:764)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.service.dto.Request` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
"customer" : {
"id" : "123",
"name" : "customer"
},
"dealer" : {
"id" : "123",
"name" : "dealer"
}')
at [Source: (PushbackInputStream); line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1588)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1213)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromString(StdDeserializer.java:311)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1495)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:207)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:197)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3601)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:376)
... 56 common frames omitted
UPDATE:
The sender app that is sending the request has a configured restTemplate with a messageConverter. How can I solve it from the receiver app?
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return createObjectMapperBuilder();
}
private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(jackson2ObjectMapperBuilder().build());
return converter;
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.setMessageConverters(Collections.singletonList(jackson2HttpMessageConverter()));
return restTemplate;
}
Your question is missing the actual restTemplate calls, so without that I'm guessing a bit...
I presume your example restTemplate is sending the JSON request body as a string.
That's what this causedBy is telling you:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.service.dto.Request (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
Take a look at these two example REST endpoints and try them locally yourself
#PostMapping("/log")
fun logRequest(#RequestBody request: Request): Request {
println("/log request: $request")
return request
}
#PostMapping("/log/string")
fun logRequestString(#RequestBody request: String): String {
println("/log/string request: $request")
return request
}
Example RestTemplate calls
val restTemplate = RestTemplate().apply {
messageConverters = listOf(jackson2HttpMessageConverter())
}
// Sending payload as a JSON Object
val requestBody = Request(Customer("id", "name"), Dealer("id", "name"))
val response = restTemplate.postForEntity(url, HttpEntity(requestBody), Object::class.java)
// Server logs:
// -> /log request: Request(customer=Customer(id=id, name=name), dealer=Dealer(id=id, name=name))
// Sending payload as a string
val headers = HttpHeaders().apply {
contentType = MediaType.APPLICATION_JSON
}
val response2 = restTemplate.postForEntity(
"$url/string",
HttpEntity(jacksonObjectMapper().writeValueAsString(requestBody), headers),
Object::class.java
)
// Server logs:
// -> /log/string request: "{\"customer\":{\"id\":\"id\",\"name\":\"name\"},\"dealer\":{\"id\":\"id\",\"name\":\"name\"}}"
If you remove this endpoint, you'll see this same error no String-argument constructor
#PostMapping("/log/string")
fun logRequestString(#RequestBody request: String): String {
println("/log/string request: $request")
return request
}
I am setting up the unit testing on my #RestController's, but It is not working correctly.
This is only a unit test to validate the answer from the method of the RestEndpoint, but It is not working, please help me because I tried a lot of configurations and read the documentation and nothing worked.
This is my endpoint:
#RestController
#RequestMapping("/clients")
public class ClientsEndpoint extends RestConfiguration {
#Autowired
private IClientService clientService;
#GetMapping(value = "/{id}")
private Object getById(#PathVariable(required = true) Long id) {
try {
Optional<Client> clientOptional = clientService.getById(id);
if (clientOptional.isPresent()) {
return clientOptional.get();
} else
return new GeneralResponse(ErrorsEnum.CLIENT_NOT_FOUND);
} catch (Exception e) {
log.log(Level.SEVERE, "Problemas al obtener datos del cliente con id: " + id, e);
return new GeneralResponse(ErrorsEnum.GENERAL_ERROR);
}
}
This is my Unit Test where I only added the mock of the endpoint and a simple call is done:
#RunWith(SpringRunner.class)
#WebMvcTest(ClientsEndpoint.class)
#ContextConfiguration(classes={ApplicationConfiguration.class})
public class QuarzoApiApplicationTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private IClientService clientService;
#Test
public void getById() throws Exception {
Client client = new Client("Test", "Test", "Test", (byte) 1,
"Test", "Test", "Test", "Test", (byte) 1,
(byte) 1, (short)1, "Test" );
Optional<Client> clientOptional = Optional.of(client);
BDDMockito.given(clientService.getById(anyLong())).willReturn(clientOptional);
this.mockMvc.perform(get("/clients/1")
.header("Authorization","Bearer " + TestUtils.createToken("miltonc")))
.andExpect(status().isOk());
}
Results I am getting after running the unit test:
2019-08-27 10:01:18.505 INFO 23606 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#7da39774, org.springframework.security.web.context.SecurityContextPersistenceFilter#5b7c8930, org.springframework.security.web.header.HeaderWriterFilter#4277127c, org.springframework.web.filter.CorsFilter#441b8382, org.springframework.security.web.authentication.logout.LogoutFilter#1a6dc589, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#40e32762, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#6f231ced, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#1df1ced0, org.springframework.security.web.session.SessionManagementFilter#2c05ff9d, org.springframework.security.web.access.ExceptionTranslationFilter#2a8a4e0c, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#27755487]
2019-08-27 10:01:18.526 INFO 23606 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2019-08-27 10:01:18.527 INFO 23606 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
2019-08-27 10:01:18.533 INFO 23606 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 6 ms
2019-08-27 10:01:18.555 INFO 23606 --- [ main] i.q.quarzoapi.QuarzoApiApplicationTests : Started QuarzoApiApplicationTests in 1.371 seconds (JVM running for 1.825)
MOCK org.springframework.test.web.servlet.MockMvc#770beef5
MockHttpServletRequest:
HTTP Method = GET
Request URI = /clients/1
Parameters = {}
Headers = [Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1NjY5MTQ0NzgsImlzcyI6IlFVQVJaTyIsInN1YiI6Im1pbHRvbmMiLCJleHAiOjE1NjY5NjQ4MDB9.hLu1Mcl_K-8smYnZnDu1ubkVTuNCkQnqhIQuypBsKBzQYxMlYlxqXhsYLCMB5iViCcDK33WXvO1Pjp15TR1ICg"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :200
Actual :404
<Click to see difference>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:55)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:82)
at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:619)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:195)
at io.quarzo.quarzoapi.QuarzoApiApplicationTests.getById(QuarzoApiApplicationTests.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
2019-08-27 10:01:18.772 INFO 23606 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
I finally solved this. I needed to include the #ComponentScan(basePackages = "io.quarzo.api.endpoints") to refer the endpoints I am trying to test.
My Test configuration:
#Configuration
#ComponentScan(basePackages = "io.quarzo.api.endpoints")
public class ApplicationConfiguration extends WebSecurityConfigurerAdapter {
After that, I had to Mock every bean I was using in the endpoints.
#MockBean(name = "clientRegistrationSingleton")
private Map<String, Client> clientsRegistering;
#MockBean
private IClientService clientService;
#MockBean
private IClientRepository clientRepository;
After that all was working ok, I can run the test and mock every Bean to test my #RestController's.
I want to serve static content from my static directory in my project resources, but I get error: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported.
I have added configured Spring Security to ignore requests starting with "/static/". I have added resource handler to WebMvcConfig to look for the static content in various different locations, I have tried removing the resource handler altogether.
I have tried adding permitAll() to the "/static/" path.
I have googled the error message and various combinations of it to find solutions. This is where I came up with the Spring Security and WebMvcConfigs. Just that these did not solve the problem for me.
Finally I have tried placing static files into different locations. The directory structure is as follows:
\resources\static\hello.css
\resources\static\css\hello.css
\resources\templates\home.html
\resources\templates\error.html
Thymeleaf works correctly, taking the templates from the templates directory.
The WebMvcConfig:
// Config Thymeleaf
private static final String VIEWS = "classpath:templates/";
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
private ISpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
return engine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix(VIEWS);
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
// Enable static resources
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("/resources/static/", "classpath:static/", "/static/", "classpath:resources/static");
}
The Spring Security config:
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**");
}
I expect to see my css file, but instead I see the error page generated by exception handler with the message "Request method 'GET' not supported"
Here is the debug trace from the point I request the css file:
2019-06-07 11:30:29.694 DEBUG 5398 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/static/css/hello.css'; against '/static/**'
2019-06-07 11:30:29.694 DEBUG 5398 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /static/css/hello.css has an empty filter list
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:200)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:419)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:365)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:65)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:401)
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1232)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1015)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:209)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
After hours of debugging I found the reason static content was not served!
It was because of a controller with mapping without the path:
#PostMapping()
After adding a path to the mapping, the configuration above works as it should.
Very weird that it messed up the serving of static files!
Big thanks to #Ganesh for his tip in Spring Boot not serving static content
thanks #Avaruuskadetti you saved hours of my time
DEBUG:
package org.springframework.web.servlet >>>> HandlerExecutionChain
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping hm = (HandlerMapping)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
I have following code:
#Test
public void testSaveValid() throws Exception {
MockRoundtrip trip = new MockRoundtrip(mockServletContext,
ContactFormActionBean.class, mockSession);
trip.setParameter("contact.email", "test#test.com");
trip.setParameter("contact.phoneNumber", "654-456-4567");
trip.execute("save");
ContactFormActionBean bean =
trip.getActionBean(ContactFormActionBean.class);
assertEquals(0,
bean.getContext().getValidationErrors().size());
PhoneNumber pn = bean.getContact().getPhoneNumber();
assertEquals("654", pn.getAreaCode());
assertEquals("456", pn.getPrefix());
assertEquals("4567", pn.getSuffix());
assertTrue(
trip.getDestination().startsWith("/ContactList.action"));
}
I encounter this error:
net.sourceforge.stripes.exception.StripesServletException: Unhandled exception in exception handler.
at net.sourceforge.stripes.exception.DefaultExceptionHandler.handle(DefaultExceptionHandler.java:158)
at net.sourceforge.stripes.controller.StripesFilter.doFilter(StripesFilter.java:249)
at net.sourceforge.stripes.mock.MockFilterChain.doFilter(MockFilterChain.java:63)
at net.sourceforge.stripes.mock.MockServletContext.acceptRequest(MockServletContext.java:255)
at net.sourceforge.stripes.mock.MockRoundtrip.execute(MockRoundtrip.java:195)
at net.sourceforge.stripes.mock.MockRoundtrip.execute(MockRoundtrip.java:207)
at stripesbook.test.stripesmock.ContactFormActionBeanTest.testSaveValid(ContactFormActionBeanTest.java:96)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
If I delete
trip.setParameter("contact.email", "test#test.com");
trip.setParameter("contact.phoneNumber", "654-456-4567");
I won't get any errors but will get following message from jUnit:
java.lang.AssertionError: expected:<0> but was:<1>
which seems logical.
This is my ContactFormActionBean class
public class ContactFormActionBean extends ContactBaseActionBean {
private static final String FORM="/WEB-INF/jsp/contact_form.jsp";
#DefaultHandler
public Resolution form() {
return new ForwardResolution(FORM);
}
public Resolution save() {
Contact contact = getContact();
contact.setUser(getUser());
contactDao.save(contact);
contactDao.commit();
getContext().getMessages().add(
getLocalizableMessage("contactSaved", contact)
);
return new RedirectResolution(ContactListActionBean.class);
}
#ValidationMethod(on="save")
public void validateEmailUnique(ValidationErrors errors) {
String email = getContact().getEmail();
Contact other = contactDao.findByEmail(email, getUser());
if (other != null && !other.equals(getContact())) {
errors.add("contact.email", new LocalizableError(
getClass().getName()+".contactEmailAlreadyUsed", other));
}
}
}
Why this error happens?
[ADDED]
This is my Setup() function where I configure mockServletContext before running any test functions:
...
private static MockServletContext mockServletContext;
private static MockHttpSession mockSession;
#BeforeClass
public static void setup() throws Exception {
mockServletContext = new MockServletContext("webmail");
Map<String,String> params = new HashMap<String,String>();
params.put("ActionResolver.Packages", "stripesbook.action");
params.put("Extension.Packages", "stripesbook.ext,"
+ "net.sourceforge.stripes.integration.spring");
mockServletContext.addFilter(StripesFilter.class,
"StripesFilter", params);
mockServletContext.setServlet(DispatcherServlet.class,
"DispatcherServlet", null);
mockSession = new MockHttpSession(mockServletContext);
mockServletContext.addInitParameter("contextConfigLocation",
"/WEB-INF/applicationContext-test.xml");
ContextLoaderListener springContextLoader =
new ContextLoaderListener();
springContextLoader.contextInitialized(
new ServletContextEvent(mockServletContext));
// Load mock user
MockRoundtrip trip = new MockRoundtrip(mockServletContext,
MockDataLoaderActionBean.class, mockSession);
trip.execute();
// Login mock user
trip = new MockRoundtrip(mockServletContext,
LoginActionBean.class, mockSession);
trip.setParameter("username", "freddy");
trip.setParameter("password", "nadia");
trip.execute("login");
}
...
I think there could be some config problems cause when I remove
<classpathentry kind="src" path="src/main/webapp" />
from class path I get different error :
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/applicationContext-test.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext-test.xml]
There seems to be a Stripes configuration problem.
Did you configure the mockServletContext correctly?
U tried with Spring ?
I have done it with :
Object test= springContextListener.getContextLoader().getCurrentWebApplicationContext().getBean("MyActionBean");
annoted your class with : #RunWith(SpringJUnit4ClassRunner.class)
concerning org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/applicationContext-test.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext-test.xml]
add :
#RunWith(SpringJUnit4ClassRunner.class to your test Class
and add Spring-test Jar to your project.
recheck the line : filterParams.put("ActionResolver.Packages", "yourapplicationpackagesPath.action");