No mapping found for HTTP request with URI [/api/transactions] in DispatcherServlet with name '' - spring-mvc

I thought this is a standard configuration. But I get a 404 back. Where else should I configure Spring Boot ?
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This is my test.
#JsonTest
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class TransactionStatisticsRestTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private JacksonTester<Transaction> json;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
final String transactionJson = json.write(transaction).getJson();
mockMvc
.perform(post("/api/transactions")
.content(transactionJson)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(transaction);
}
}
Request being made is
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/transactions
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8]}
Body = {"amount":"12.3343000000000007077005648170597851276397705078125","timestamp":"2010-10-02T12:23:23Z[UTC]"}
Session Attrs = {}
Handler:
Type = null
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 = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Update : I added a component scan pointing to a base package. I don't see that error now. Please see the comments where there is an answer.

As in the comment section ,there was only requirement was to bind a component scan base package location .
#Component scan -->Configures component scanning directives for use with #Configuration classes. Provides support parallel with Spring XML's element.
Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.

Please share your project folder architecture. It might be possible that your controller package is out of the main class package. That's why it is showing 404.
This code :
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This should be into your main package where
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
this main class resides.
I hope, this will help.

Seems using #JsonTest does not even allow to load Application Context, results mapping is not loaded and its throw 404 so #JsonTest is not a replacement for #SpringBootTest, it is a way to easily test json serialization/de-serialization.
As per documentation:
you can use the #JsonTest annotation. #JsonTest auto-configures the
available supported JSON mapper, which can be one of the following
libraries:
Jackson ObjectMapper, any #JsonComponent beans and any Jackson Modules
Gson
Jsonb
If by using Gson and removing #JsonTest your test run fine..(add Gson Dependency in pom)
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class DemoKj01ApplicationTests {
#Autowired
private MockMvc mockMvc;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
//final String transactionJson = json.write(transaction).getJson();
Gson gson = new Gson();
String jsonRequest = gson.toJson(transaction);
mockMvc
.perform(post("/api/transactions")
.content(jsonRequest)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}

It is beacause of the trailing slas in #RequestMapping(value = "/transactions/", method = RequestMethod.POST)
Remove it and it will be ok : value = "/transactions/" => value = "/transactions"

Related

Unable to Mock the Database call handled by Spring JPA framework while writing test Case for Controller Class

Spring MVC converts the id from path to corresponding object by making call to JpaRepository's findById method. For example see getVersionTree() method.
public class Controller {
#NonNull
private final MyService service;
#NonNull
private final MyAssembler assembler;
#GetMapping(path = VERSION_TREE_MAPPING, produces = MediaTypes.HAL_JSON_UTF8_VALUE)
public HttpEntity<?> getVersionTree(#PathVariable("id") MappingDocument mappingDocument, Pageable pageable, PagedResourcesAssembler<VersionNode> pagedResourcesAssembler) {
Page<VersionNode> versionNodes = service.getVersionTreeFor(mappingDocument, pageable);
return new ResponseEntity<>(pagedResources, HttpStatus.OK);
}
While testing, SpringMVC throws " Failed to convert value of type 'java.lang.String' to required type 'com.rbc.dna.dtl.mappingdocument.MappingDocument'". I am mocking jpaRepository.findById() method. Test Code is as follows:
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private MyRepository repository;
#Mock
MyController controller;
#MockBean
private MyServiceImpl serviceImpl;
#Test
public void testMethod() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).apply(SecurityMockMvcConfigurers.springSecurity()).build();
MappingDocument mappingDocumentl=MappingDocument.builder().id(17L).build();
Mockito.when(repository.findById(17L)).thenReturn(Optional.of(mappingDocumentl));
Mockito.when(serviceImpl.getVersionTreeFor(mappingDocument,pageable)).thenReturn(pagedResponse);
mockMvc.perform(MockMvcRequestBuilders.get("/abc/17/def").param("page","0").param("size","20").contentType(MediaTypes.HAL_JSON_UTF8_VALUE)
.with(authentication(getOauthTestAuthentication()))
.sessionAttr("scopedTarget.oauth2ClientContext", getOauth2ClientContext()))
.andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
Your rest Controller take #PathVariable("id") MappingDocument mappingDocument as variable in your path, but in mockMvc.perform you are passing a simple String/Number or something that doesn't bind with MappingDocument.
Try to replace
public HttpEntity<?> getVersionTree(#PathVariable("id") MappingDocument mappingDocument...
with
public HttpEntity<?> getVersionTree(#PathVariable("id") Long idMappingDocument, ...
If you want to keep your object in #PathVariable you need to change
#GetMapping(path = VERSION_TREE_MAPPING)
To parse an object as PathVariable you need to have path that represent property of your object.
For example if you have
class Person {
String name;
String address;
//getters and setters
}
and you need to define a controller as follow:
#GetMapping(path = "/person/{name}/{address}", produces = MediaTypes.HAL_JSON_UTF8_VALUE)
public HttpEntity<?> getVersionTree(Person person) {
Where {name} and {address} must bind Person properties

Consume SOAP WS in Spring MVC

I'm trying to consume some SOAP web services within a spring boot application. I've imported the ws's stubs and I've followed WebServiceTemplate, as explained here. Unfortunately, when making requests I get an exception:
2017-01-13 12:13:47.146 ERROR 1300 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.oxm.MarshallingFailureException: JAXB marshalling exception; nested exception is javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: no se ha podido canalizar el tipo "com.dashboard.dto.ComprobarSolicitud" como un elemento, porque le falta una anotaciĆ³n #XmlRootElement]] with root cause
The "ComprobarSolicitud" class is the following one:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "comprobarSolicitud", propOrder = {
"username",
"passwd",
"nif",
"fechaDesde",
"fechaHasta",
"cantidad"
})
public class ComprobarSolicitud {
protected String username;
protected String passwd;
protected String nif;
protected String fechaDesde;
protected String fechaHasta;
protected int cantidad;
// ...getters and setters
WebServiceGatewaySupport class:
public class PerClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(PadronClient.class);
public ComprobarSolicitudResponse comprobarSolicitudes(String pNif, LocalDate pFechaInicio, LocalDate pFechaFin){
ComprobarSolicitud request = new ComprobarSolicitud();
// .. set operations to request
ComprobarSolicitudResponse response = (ComprobarSolicitudResponse) getWebServiceTemplate()
.marshalSendAndReceive(
"https://ws.dir.com:8444/PerExterno/perExterno",
request,
new SoapActionCallback("http://service.ws.per.company.com/ExternalWS/comprobarSolicitudResponse"));
return response;
}
}
Configuration class:
#Configuration
public class PerConfiguration {
#Bean
public Jaxb2Marshaller marshaller(){
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.dashboard.dto.per");
return marshaller;
}
#Bean
public PerClient padronClient(Jaxb2Marshaller marshaller){
PerClient client = new PerClient();
client.setDefaultUri("https://ws.dir.com:8444/PerExterno");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
Should I create a custom marshaller? But, how? I've found this, where it's said that in case #XmlRootElement annotation is missing, I should wrap it in an instance of JAXBElement.
Thank you
SOLUTION!
The exception was self-explanatory, and the solution was straightforward, as just PerClient class was necessary to be modified as follows:
public class PerClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(PadronClient.class);
public ComprobarSolicitudResponse comprobarSolicitudes(String pNif, LocalDate pFechaInicio, LocalDate pFechaFin){
ComprobarSolicitud request = new ComprobarSolicitud();
// .. set operations to request
ObjectFactory of = new ObjectFactory();
JAXBElement<ComprobarSolicitud> reqjaxb = of.createComprobarSolicitud(request);
#SuppressWarnings("unchecked")
JAXBElement<ComprobarSolicitudResponse> response = (ComprobarSolicitudResponse) getWebServiceTemplate()
.marshalSendAndReceive(
"https://ws.dir.com:8444/PerExterno/perExterno",
reqjaxb ,
new SoapActionCallback("http://service.ws.per.company.com/ExternalWS/comprobarSolicitudResponse"));
return response.getValue();
}
}

Inconsistent auto-decoding with spring #RequestParam

I have a normal spring #Controller which takes an URL-encoded string as parameter:
#RequestMapping(value = "/wechat/browser", method = GET)
public void askWeChatWhoTheUserIs(#RequestParam(name = "origin") String origin,
HttpServletResponse response) throws IOException {
//omitted codes
}
When I debug the spring boot application and test the endpoint with browser:
curl http://localhost:8080/wechat/browser\?origin\=http%3A%2F%2Fwww.example.com%2Findex.html%3Fa%3Db%23%2Froute
The origin got decoded automatically and equal to http://www.example.com/index.html?a=b#/route
But when I wrote a spring mvc test:
#RunWith(SpringRunner.class)
#WebMvcTest(WeChatOauthController.class)
public class WeChatOauthControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void itShouldRedirectToWeChatToFinishOauthProtocol() throws Exception {
String origin = "http://www.example.com/index.html?a=b#/route";
String encodedOrigin = URLEncoder.encode(origin, "UTF-8");
this.mvc.perform(get("/wechat/browser")
.param("origin", encodedOrigin))
.andDo(print())
//omitted codes
}
}
When I debug this test and the controller, the origin was not decoded this time. Just wondering why it behaves differently in these two cases.
When supplying a request parameter with the Spring MVC Test framework, there is no need to manually encode the parameter's value since there is no physical HTTP request.
So, just use the original raw value in your test, and it should work fine.
In other words, use this:
#RunWith(SpringRunner.class)
#WebMvcTest(WeChatOauthController.class)
public class WeChatOauthControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void itShouldRedirectToWeChatToFinishOauthProtocol() throws Exception {
this.mvc.perform(get("/wechat/browser")
.param("origin", "http://www.example.com/index.html?a=b#/route"))
.andDo(print())
//omitted codes
}
}
You can use this method , thus there will be proper decoding
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WeChatOauthControllerTest {
#LocalServerPort
private int port;
TestRestTemplate restTemplate = new TestRestTemplate();
#Test
public void testAmpersandEncoded(){
ResponseEntity<String> response =
restTemplate.exchange(createURI("%26"),HttpMethod.GET, null, String.class);
assertEquals(response.getStatusCode(), HttpStatus.OK);
}
private URI createURI(String param){
URI uri = null;
String url = "http://localhost:"+ port +"/location?query=" + param;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
log.error(e.getMessage());
}
return uri;
}

mvc controller test with session attribute

I'm trying to test a method with this signature:
#Autowired
HttpSession http_Session;
#RequestMapping(method=RequestMethod.GET, value="/search/findByName")
public #ResponseBody List<Map> search(#RequestParam(value="name", required=true) String name){
Integer user_id = http_Session.getAttribute("userinfo");
}
userinfo is a class which contains informations about the user and set in session scope when the user logged in.but when I try the test :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:/META-INF/applicationContext.xml"})
public class userControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
#Test
public void userTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/search/findByName").param("name", "bob"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk());
}
The problem is the userinfo class attribute is set in another method so when i try to access it in this method i got a NullPointerException , and with Autowiring the httpSession i got a new Session for each method i have to test.
What should i do with the session attribute, my method doesn't accept a session parameter , and for each test it create a WebApplicationContext with a new session.
Try this :
HashMap<String, Object> sessionattr = new HashMap<String, Object>();
sessionattr.put("userinfo", "XXXXXXXX");
mockMvc.perform(MockMvcRequestBuilders.get("/search/findByName").sessionAttrs(sessionattr).param("name", "bob"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk());
You could also share a session across different requests:
import static org.springframework.test.web.servlet.setup.SharedHttpSessionConfigurer.sharedHttpSession;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(this.webApplicationContext)
.apply(sharedHttpSession()) // use this session across requests
.build();
}
note: this session will be shared among requests performed against the same MockMvc instance only.

How to setup OAuth2RestTemplate (Post Updated)

I am not sure if I have OAuth2RestTemplate configured correctly. I am getting the following error when I run the tester class.
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#1df3248: defining beans [propertyConfigurer,dataSource,transactionManager,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,emf,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,accountRepository,questionRepository,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#0,org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0,jpaQuestionService,jpaAccountService,passwordEncoder,accountHelper,tradeConfig,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#1,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,baseOAuth2ProtectedResourceDetails,oAuth2ProtectedResourceDetails,accessTokenRequest,oAuth2ClientContext,oAuth2RestTemplate]; root of factory hierarchy
Exception in thread "main" error="access_denied", error_description="Unable to obtain a new access token for resource 'null'. The provider manager is not configured to support it."
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:146)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:216)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:168)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:89)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:442)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:123)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:385)
at com..main(Tester.java:44)
Classes
#Configuration
public class AppConfig {
#Bean
//#Scope(value="singleton", proxyMode=ScopedProxyMode.INTERFACES)
public BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails(){
BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails = new BaseOAuth2ProtectedResourceDetails();
baseOAuth2ProtectedResourceDetails.setClientId(clientId);
baseOAuth2ProtectedResourceDetails.setClientSecret(clientSecret);
return baseOAuth2ProtectedResourceDetails;
}
#Bean
public DefaultAccessTokenRequest accessTokenRequest(){
return new DefaultAccessTokenRequest();
}
#Bean
public OAuth2ClientContext oAuth2ClientContext(){
return new DefaultOAuth2ClientContext(accessTokenRequest());
}
#Bean
public OAuth2RestTemplate oAuth2RestTemplate(){
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(baseOAuth2ProtectedResourceDetails(),oAuth2ClientContext());
return restTemplate;
}
}
Tester Class
public class Tester {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath*:jpa-app-context.xml");
ctx.refresh();
EntityManagerFactory emf = (EntityManagerFactory) ctx.getBean("emf");
EntityManager em = emf.createEntityManager();
TransactionSynchronizationManager.bindResource(emf , new EntityManagerHolder(em));
OAuth2RestTemplate oAuth2RestTemplate = (OAuth2RestTemplate) ctx.getBean("oAuth2RestTemplate");
//OAuth2RestTemplate oAuth2RestTemplate = ctx.getBean(OAuth2RestTemplate.class);
String uri="https:api..";
Object obj = oAuth2RestTemplate.exchange(uri, HttpMethod.POST, null, Object.class);
System.out.println("Tester Object: "+ obj.toString());
}
}
I faced the same exception, but with another protected resource type.
Generally, the exception raises only when AccessTokenProviderChain can't find an appropriate *AccessTokenProvider for particular *ProtectedResourceDetails instance. Meaning, when you try to do the following:
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(url);
resource.setClientId(clientId);
resource.setClientSecret(secret);
resource.setGrantType("password");
return resource;
The code expects a client_credentials grant type since we use a ClientCredentialsResourceDetails, but we pass password value.
Here the code that worked in my case:
private OAuth2ProtectedResourceDetails withOAuth2Authentication(final String url, final String clientId, final String secret) {
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(url);
resource.setClientId(clientId);
resource.setClientSecret(secret);
// here you can provide additional properties such as scope etc.
return resource;
}
#Bean
RestTemplate callbackClientV2() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(
withOAuth2Authentication(v2ServerUrl, v2Username, v2Password),
new DefaultOAuth2ClientContext(atr)
);
}

Resources