I am new to MongoDB and I am trying to use it with my SpringBoot application. I have followed my tutorials online and have downloaded their code and got it execute.
However for whatever reason my project fails to be able to print out
RequestMappingHandlerMapping : Mapped “{[/findAllBooks/{id}],methods=[GET]}”
I was wondering if anyone would be able to advise me if it is due to the nature of my project structure .
I wasn’t sure if my SpringBootMain could see my Controller class.
My project structure is best viewed here
https://github.com/emuldrew855/backend/tree/A/B-Testing/src/main/java/com/ebay/queens/demo
My Controller class
package com.ebay.queens.demo.resource;
#RestController
#RequestMapping("/v2")
public class UserController {
#Autowired
private UserRepository userRepository;
#PostMapping("/AddUser")
public String saveUser(#RequestBody User user) {
userRepository.save(user);
return "Added user with id: " + user.getId();
}
#GetMapping("/all")
public List<User> getAll(){
List<User> users = this.userRepository.findAll();
return users;
}
}
My main class
package com.ebay.queens.demo;
#SpringBootConfiguration
#SpringBootApplication
public class SpringBootMain implements CommandLineRunner {
#Autowired
private TokenUtilityClass tokenUtilityClass;
#Bean ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class, Login.class, SignUp.class, Paypal.class); }
#Override
public void run(String... args) throws Exception {
// test.authenticationToken();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootMain.class, args);
}
}
I've figured out why is not working... You are using 2 different WebService API which are incompatible...
Spring-Boot has native API to work with API Rest with #RestController annotation. You don't need to use Glassfish server.
Solution #1
From SpringBootMain remove #Bean ResourceConfig resourceConfig() {...}. Now, you API /v2 will work as expected.
Your API /v1 won't work because it uses other library. You need to change #Path to #GetMapping or #PostMapping and add #RestController into your Version1Api class.
Solution #2
You ignore Spring-Boot native Rest API and implement Glassfish Server.
Add UserController.class reference
#Bean ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class, Login.class, SignUp.class, Paypal.class, UserController.class); }
For UserController change #RestController to #Path("/v2")
#Path("/v2")
public class UserController {
#Autowired
private UserRepository userRepository;
#POST
#Path("/AddUser")
#Produces(MediaType.TEXT_PLAIN)
public String saveUser(#RequestBody User user) {
userRepository.save(user);
return "Added user with id: " + user.getId();
}
#GET
#Path("/all")
#Produces(MediaType.APPLICATION_JSON)
public List<User> getAll(){
List<User> users = this.userRepository.findAll();
return users;
}
}
Now both API will work as expected
Related
I know there are a lot of similar issues here on stackoverflow, but none of this fixes my issue.
I want to expose some methods from my Spring Boot Repositories as Webservice, but one Repository randomly ^(1) only returns 404 on all Methods.
We are talking about following classes
#Component
public class CustomRepositoryRestConfigurer implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.disableDefaultExposure();
}
}
#RepositoryRestResource(exported = true)
public interface TransactionRepository extends JpaRepository<Transaction, Long> {
#Query("select t from Transaction where ....")
#RestResource(exported = true) // true is default
Page<Transaction> findAllByQuery(
// more stuff
#Param("text") String text,
Pageable pageable);
void delete(Transaction entity); // should not be exposed!
}
And following tests will fail:
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = Application.class)
public class SampleTest {
#Autowired
private MockMvc mvc;
#Test
public void check_profile_is_ok() throws Exception {
mvc.perform(get("/")
// fails...
.andExpect(jsonPath("$._links.transactions").value(notNullValue()))
}
#Test
public void check_access() throws Exception {
mvc.perform(get("/transactions/search/findAllByQuery")
// fails...
.andExpect(status().isOk());
}
}
When remove config.disableDefaultExposure(); the Testcase will pass, but then all Endpoints are exposed - and I don't want this.
Is there any configuration I'm missing?
I have a second repository CategoryRepository and everything is same but working.
Okay I solved it by myself. I had (falsely) a second Repository on the Transaction Entity.
I do have a requirement to create a spring boot and angular application without the use of any database, not even in memory db.The application should accept user input and be displayed in Angular.
// The controller class looks like bellow
#CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
#RestController
#RequestMapping({ "/api" })
public class UserController {
#Autowired
userService userService;
#PostMapping
public Computer create(#RequestBody Computer computer) {
return computerService.save(computer);
}
#GetMapping
public List<Computer> findAll() {
return computerService.findAll();
}
}
#Service
public class UserServiceImpl implements UserService{
private static List<User> users = new ArrayList<>();
private static int Count = 3;
static {
user.add(new User(34, 2000.22,new Date()));
user.add(new User(60, 3000.44,new Date()));
user.add(new User(45, 2000.22,new Date()));
}
And of course there is a userService class with the methods in the implementation class.Is it possible to use hashMap to accept data and then display in Angular without first storing the data in a file or database?. Any ideas would be highly appreciated.
}
I have a spring boot api. My problem is how do I actually create a page in my app?
I created a page in /resources/static/templates/index.html
and when I get to /api/lol I just see a string saying index and not the page.
How do I do this?
#RequestMapping("/api")
#RestController
public class Controller {
#Autowired
JdbcTemplate jdbcTemplate;
#GetMapping("/lol")
String lol() {
return "index";
}
}
You annotated your class with #RestController.
What you want is a MVC Controller, so replace it with #Controller.
Here is an example of the documentation :
#Controller
public class HelloController {
#GetMapping("/hello")
public String handle(Model model) {
model.addAttribute("message", "Hello World!");
return "index";
}
}
Put your index.html in src/main/resources/static
and then in your controller
#RequestMapping("/api")
#RestController
public class Controller {
#Autowired
JdbcTemplate jdbcTemplate;
#GetMapping("/lol")
String lol() {
return "index.html";
}
}
I've faced with a situation when I was testing websocket endpoint and when I used rollbacked transaction to rollback all changes made during the test I've got an unpredictable behaviour.
I created an simple example to show us what is happenning.
User.java
#Entity(name = "USERS")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
// getters,setters ommitted
}
UserRepository.java
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
User getUserByName(String name);
}
UserService.java
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public User getUser(String name){
return userRepository.getUserByName(name);
}
}
Websocket specific
#Configuration
#EnableWebSocket
public class Config implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(userHandler(), "/api/user");
}
#Bean
UserHandler userHandler(){
return new UserHandler();
}
}
UserHandler.java
public class UserHandler extends TextWebSocketHandler{
#Autowired
private UserService userService;
#Override
public void afterConnectionEstablished(WebSocketSession session) {
try {
HttpHeaders headers = session.getHandshakeHeaders();
List<String> userNameHeader = headers.get("user_name");
if (userNameHeader == null || userNameHeader.isEmpty()){
session.sendMessage(new TextMessage("user header not found"));
return;
}
String userName = userNameHeader.get(0);
User user = userService.getUser(userName);
if (user == null){
session.sendMessage(new TextMessage("user not found"));
return;
}
session.sendMessage(new TextMessage("ok"));
} catch (Throwable e) {
e.printStackTrace();
}
}
}
And eventually UserHandlerTest.java. I use okHttp3 as a websocket test client:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class UserHandlerTest {
#Autowired
private UserRepository userRepository;
private User testUser;
private BlockingQueue<String> messages = new LinkedBlockingQueue<>();
private OkHttpClient okHttpClient;
#Before
public void setUp(){
okHttpClient = new OkHttpClient();
testUser = new User();
testUser.setName("test");
userRepository.save(testUser);
}
#Test
public void testThatUserExist(){
Request request = new Request.Builder()
.url("ws://127.0.0.1:8080/api/user")
.header("user_name","test")
.build();
WebSocket ws = okHttpClient.newWebSocket(request,new MsgListener());
String msg = null;
try {
msg = messages.poll(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println(e);
}
Assert.assertNotNull(msg);
Assert.assertThat(msg,is("ok"));
}
private class MsgListener extends WebSocketListener{
#Override
public void onOpen(WebSocket webSocket, Response response) {
System.out.println("onOpen:"+response.message());
}
#Override
public void onMessage(WebSocket webSocket, String text) {
System.out.println("onMessage:"+text);
messages.add(text);
}
}
}
And my test aplication.properties:
spring.jpa.database=postgresql
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.username=postgres
spring.datasource.password=******
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/USERS_TEST
All the code above is ok and test is passed. But suppose I don't want to mess the db so I use #Transactional on all test methods to rollback all changes after the test is done.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#Transactional
public class UserHandlerTest {
// the same as above
}
And then UserRepository does not find the user saved in setUp test method.
java.lang.AssertionError:
Expected: is "ok"
but: was "user not found"
I tried to reproduce the same situation on the rest endpoint and it works there. Let's see.
UserController.java
#RestController
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "/api/user")
public #ResponseBody User getUser(String name){
return userService.getUser(name);
}
}
And UserControllerTest.java:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#Transactional
public class UserControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Autowired
private UserRepository userRepository;
private User testUser;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
testUser = new User();
testUser.setName("test");
userRepository.save(testUser);
}
#Test
public void testThatUserExist() throws Exception {
mockMvc.perform(get("/api/user")
.param("name","test")
).andExpect(status().isOk())
.andExpect(jsonPath("name").value(is("test")));
}
}
Last test is passed and the transaction is rolled back.
I want to see the same behaviour when I test websocket endpoint.
Could someone point me out where the differencies here? And Why Spring does it?
This is essentially a duplicate of Spring Boot #WebIntegrationTest and TestRestTemplate - Is it possible to rollback test transactions?.
I want to see the same behaviour when I test websocket endpoint.
You can't. When you declare #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT), Spring Boot starts an embedded Servlet container.
That's why there are two different transactions if you additionally annotate your tests with #Transactional:
test-managed
spring-managed
For your use case, you will need to stop using #Transactional in your test and start manually resetting the state of the database after your test completes. For that I'd recommend the use of #Sql with the AFTER_TEST_METHOD execution phase. See How to execute #Sql before a #Before method for an example.
Regards,
Sam (author of the Spring TestContext Framework)
I am implementing rest services with Spring Boot. The entity classes are defined in a separate package. So I added that with Component annotation in Application.java.
#Configuration
#EnableAutoConfiguration
#ComponentScan("org.mdacc.rists.cghub.model")
#EnableJpaRepositories(basePackages = "org.mdacc.rists.cghub.model")
public class Application
{
public static void main( String[] args )
{
SpringApplication.run(Application.class, args);
}
}
Here is my controller class:
// SeqController.java
#RestController
public class SeqController {
#Autowired
private SeqService seqService;
#RequestMapping(
value = "/api/seqs",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<SeqTb>> getSeqs() {
List<SeqTb> seqs = seqService.findAll();
return new ResponseEntity<List<SeqTb>>(seqs, HttpStatus.OK);
}
}
I also created a JPA data repository that extends JPARepository in which I added custom query code.
// SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
#Query("SELECT s FROM SeqTb s where s.analysisId = :analysisId")
public SeqTb findByAnalysisId(String analysisId);
}
Below is the servicebean class that implements a service interface
// SeqServiceBean.java
#Service
public class SeqServiceBean implements SeqService {
#Autowired
private SeqRepository seqRepository;
#Override
public List<SeqTb> findAll() {
List<SeqTb> seqs = seqRepository.findAll();
return seqs;
}
public SeqTb findByAnalysisId(String analysisId) {
SeqTb seq = seqRepository.findByAnalysisId(analysisId);
return seq;
}
}
When I started the application and type the following url in the browser "http://localhost:8080/api/seqs" , I got 404 error. What did I miss?
Edit #1:
I decided to take out the JPA repository stuff and change the controller class to the following:
#RestController
//#RequestMapping("/")
public class SeqController {
private static BigInteger nextId;
private static Map<BigInteger, Greeting> greetingMap;
private static Greeting save(Greeting greeting) {
if(greetingMap == null) {
greetingMap = new HashMap<BigInteger, Greeting>();
nextId = BigInteger.ONE;
}
greeting.setId(nextId);
nextId = nextId.add(BigInteger.ONE);
greetingMap.put(greeting.getId(), greeting);
return greeting;
}
static {
Greeting g1 = new Greeting();
g1.setText("Hello World!");
save(g1);
Greeting g2 = new Greeting();
g1.setText("Hola Mundo!");
save(g2);
}
#RequestMapping(
value = "/api/greetings",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Collection<Greeting>> getGreetings() {
Collection<Greeting> greetings = greetingMap.values();
return new ResponseEntity<Collection<Greeting>>(greetings, HttpStatus.OK);
}
}
When I started the application and put "localhost:8080/api/greetings" in my browser I still got 404.
==>Did you make sure that your Spring Boot application class and your Rest Controller are in the same base package? For Example if your package for Spring Boot application class is com.example.demo, then your Rest Controller should be in same base package as com.example.demo.controller.
==>I think that is the reason boot is unable to map to the uri of your rest controller. Because #SpringBootApplication has #ComponentScan and #Configuration embedded in it already. Try doing this. I hope it works.
If spring boot starter web is not there in your pom.xml then add the same as the reason could be the code not being able to map the endpoints.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
The first thing I would try is to put #RequestMapping("/") on the class definition of the controller. Keep the same value on the method.
Another thing, unrelated to your problem, is that you do not need to define that custom query. JPA is actually smart enough to do the query you defined just by using that method name. Check out the findByLastName example here: https://spring.io/guides/gs/accessing-data-jpa/.