#RestController always return 404 when called - java

I have two controllers (UserController and ClientController), both of the controllers are located on the same package (com.myapp.controllers.identity), and my main application file located on the parent package (com.myapp).
I create the ClientController first and it works fine. Later on, I create the UserController. When I called the UserController, it always returns 404.
Here is the snippet of my controllers' files and main application file
Application.java
package com.myapp;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ClientController.java
package com.myapp.controllers.identity;
#RestController
#RequestMapping(value = "/api/identity")
#Validated
public class ClientController {
#GetMapping(value = "/clients/{clientId}")
public ResponseEntity<?> getClientByClientId(#PathVariable("clientId") String clientId) {
}
}
UserController.java
package com.myapp.controllers.identity;
#RestController(value = "UserController")
#RequestMapping(value = "/api/identity")
#Validated
public class UserController {
public static final Logger logger = LoggerFactory.getLogger(UserController.class.getName());
#Autowired
private UserService userService;
#GetMapping(value = "/users/client/:clientId")
public ResponseEntity<?> getAllUsersByClientId(#PathVariable String clientId)
{
}
}
Can anybody help me solve it?

There is one simple problem in your userControler, and that is #GetMapping(value = "/users/client/:clientId") your format of capturing parameter
this kind of parameter is not supported in spring as #chrylis -on strike- mentioned
package com.myapp.controllers.identity;
#RestController(value = "UserController")
#RequestMapping(value = "/api/identity")
#Validated
public class UserController {
public static final Logger logger = LoggerFactory.getLogger(UserController.class.getName());
#Autowired
private UserService userService;
#GetMapping(value = "/{clientId}")
public ResponseEntity<?> getAllUsersByClientId(#PathVariable String clientId)
{
}
}

Related

MockRestServiceServer does not involve error handler annotated with #RestControllerAdvice

I am trying to write integration test in Spring Boot 2.7.3.
This application should scrape data from some external REST service. To call rest service I am using RestTemplate from spring framework
#Service
public class UserService {
private final RestTemplate restTemplate;
private String url = "http://localhost:3000/user/{name}";
#Autowired
public UserService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public UserData getUserInfo(UserInfo userInfo) {
return restTemplate.getForObject(url, UserData.class, userInfo.getName());
}
}
This service is used by rest controller that is accepting name as a path parameter (we can call it localhost:8080/info?name=joe)
#RestController
public class UserController {
private UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping("/info")
#ResponseBody
public UserData getUserData(UserInfo userInfo) {
return userService.getUserInfo(userInfo);
}
}
I have created controller annotated with #RestControllerAdvice and handling method that should be invoked when throwing HttpClientErrorException
#RestControllerAdvice
public class UserExceptionHandler {
#Autowired
public UserExceptionHandler(){
}
#ExceptionHandler(value = {HttpClientErrorException.class})
public ErrorResponse clientErrorHandle(HttpClientErrorException e) {
return ErrorResponse.builder().error("could not retrieve user info").build();
}
}
Problem is that below test is not hitting UserExceptionHandler
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {
#Autowired
RestTemplate restTemplate;
#Autowired
UserController userController;
private MockRestServiceServer mockServer;
#BeforeEach
public void setUp() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void testGetRootResourceOnce() {
mockServer.expect(once(), requestTo("http://localhost:3000/user/joe"))
.andRespond(withBadRequest().contentType(MediaType.APPLICATION_JSON).body("{\"error\": \"Error while getting data\"}"));
UserInfo userInfo = UserInfo.builder().name("joe").build();
UserData userData = userController.getUserData(userInfo);
mockServer.verify();
assertThat(userData).isNotNull();
}
}
While debugging I set breakpoint on UserExceptionHandler constructor so I assume that it has been created.
But why method clientErrorHandle was not invoked?
I see that RestTemplate throw HttpClientErrorException (please see picture below) so it should be intercepted by my handler, but it was not

Spring Unit Test Rest Controller By Setting Private Fields

I have a simple Rest Controller as below
#RestController
public class HealthController {
private static final CustomLogger logger = CustomLogger.getLogger(HealthController.class.getName());
private HealthService healthService;
#Autowired
public HealthController(HealthService healthService) {
this.healthService = healthService;
}
#RequestMapping(value = "/health", method = RequestMethod.GET)
public ResponseEntity<?> healthCheck() {
return healthService.checkHealth();
}
}
The service class is below
#Service
public class HealthService {
private static final CustomLogger logger = CustomLogger.getLogger(HealthController.class.getName());
public ResponseEntity<?> checkHealth() {
logger.info("Inside Health");
if (validateHealth()) {
return new ResponseEntity<>("Healthy", HttpStatus.OK);
} else {
return new ResponseEntity<>("Un Healthy", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
boolean validateHealth() {
return true;
}
}
The corresponding unit test for the controller class as below
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HealthController.class)
public class HealthControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HealthService healthService;
#Test
public void checkHealthReturn200WhenHealthy() throws Exception {
ResponseEntity mockSuccessResponse = new ResponseEntity("Healthy", HttpStatus.OK);
when(healthService.checkHealth()).thenReturn(mockSuccessResponse);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/health").accept(
MediaType.APPLICATION_JSON);
MvcResult healthCheckResult = mockMvc
.perform(requestBuilder).andReturn();
Assert.assertEquals(HttpStatus.OK.value(), healthCheckResult.getResponse().getStatus());
}
}
The problem I have is my CustomLogger. Since it has external dependencies am having issues in trying to test this.The same kind of logger is present in my service classes too.
How can I test such a class. I tried the below stuffs
Created a custom class name CustomLoggerForTest under test. Used
ReflectionTestUtils.setField(healthService, "logger", new CustomerLoggerForTest(HealthService.class.getName()));
in the setUp. But it did not help. Using this we cannot set the static fields hence tried even converting them to be non-static
Tried with mocking the CustomLogger in setup as below
mockStatic(CustomLogger.class); when(CustomLogger.getLogger(any())) .thenReturn(new CustomLoggerForTest(HealthController.class.getName()));
But no luck.
Is there anything that am doing wrong that is causing this?

How to get data from another service using Feign Client Spring Boot (ERROR: 406)

When I call another Service (User-service) from one service (API-gateway) Using Feign Client, I'm getting an Error
There are two services
User-service
API-gateway
In my API-gateway
FeignClient
#FeignClient(contextId = "user-by-email",name = "user-service")
#Service
public interface UserByEmail {
#RequestMapping(value = "/email/{email}", consumes= MediaType.APPLICATION_JSON_VALUE)
User findByEmail(#PathVariable("email") String email);
}
Controller
#RequestMapping("/test")
public class TestController {
#Autowired
private UserByEmail userByEmail;
#GetMapping(value = "/{email:.+}")
public ResponseEntity testUser(#PathVariable("email") String username) {
return ResponseEntity.ok(userByEmail.findByEmail(username));
}
}
I need to call the following (User-service)
Controller
#EnableFeignClients
#RestController
public class UserController extends BaseController<User> {
#Autowired
private UserService userService;
#PostConstruct
public void binder() {
init(this.userService);
}
#GetMapping(value = "/email/{email}")
public ResponseEntity findByEmail(#PathVariable("email") String email) {
return ResponseEntity.ok(userService.findByEmail(email));
}
}
Repository
#Override
public User findByEmail(String email) {
Query query = new Query(Criteria.where("email").is(email).and("status").is(1));
return mongoOperations.findOne(query, User.class);
}
Service
#Override
public User findByEmail(String email) {
return userDao.findByEmail(email);
}
The Error I'm getting is ..
<Map>
<timestamp>1583924335777</timestamp>
<status>406</status>
<error>Not Acceptable</error>
<message>Could not find acceptable representation</message>
<trace>org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:246)
Can anyone please explain What is wrong with my code, And give your valuable solutions
(Basically I need to create Security in API-gateway, in order to control the access of other services)
Try Implementing Feign client as below:
#FeignClient(name = "user-service")
public interface UserByEmail {
#RequestMapping(method = RequestMethod.GET, value = "/email/{email}", consumes = "application/json")
User findByEmail(#PathVariable("email") String email);
}
also make sure the Fields passed over JSON matchers User POJO.
I've got the solution to my Question
FeignClient
#FeignClient(contextId = "user-by-email",name = "user-service")
#Service
public interface UserByEmail {
#RequestMapping(value = "/email/{email}", consumes= MediaType.APPLICATION_JSON_VALUE)
User findByEmail(#RequestParam("email") String email);
}
Controller
#RequestMapping("/test")
public class TestController {
#Autowired
private UserByEmail userByEmail;
#GetMapping(value = "/{email}")
public ResponseEntity testUser(#RequestParam("email") String username) {
return ResponseEntity.ok(userByEmail.findByEmail(username));
}
}
In User-service
Controller
#EnableFeignClients
#RestController
public class UserController extends BaseController<User> {
#Autowired
private UserService userService;
#PostConstruct
public void binder() {
init(this.userService);
}
#GetMapping(value = "/email/{email}")
public ResponseEntity findByEmail(#RequestParam("email") String email) {
return ResponseEntity.ok(userService.findByEmail(email));
}
}

How to set the context path in Spring Boot WebMvcTest

I'm trying to test my Rest controllers from my Spring Boot application and want the controllers to be available under the same path as in production.
For example I have the following Controller:
#RestController
#Transactional
public class MyController {
private final MyRepository repository;
#Autowired
public MyController(MyRepository repository) {
this.repository = repository;
}
#RequestMapping(value = "/myentity/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<Resource<MyEntity>> getMyEntity(
#PathVariable(value = "id") Long id) {
MyEntity entity = repository.findOne(id);
if (entity == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(entity, HttpStatus.OK);
}
}
Within my application.yml I have configured the context path for the application:
server:
contextPath: /testctx
My test for this controller looks as follows:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = MyController.class, secure=false)
public class MyControllerTest {
#Autowired
private MyRepository repositoryMock;
#Autowired
private MockMvc mvc;
#Test
public void testGet() throws Exception {
MyEntity entity = new MyEntity();
entity.setId(10L);
when(repositoryMock.findOne(10L)).thenReturn(entity);
MockHttpServletResponse response = this.mvc.perform(
MockMvcRequestBuilders.get("/testctx/myentity/10"))
.andReturn().getResponse();
assertEquals(response.getStatus(), 200);
}
#TestConfiguration
public static class TestConfig {
#Bean
MyRepository mockRepo() {
return mock(MyRepository.class);
}
}
}
This test fails since the status code is 404 for the call. If I call /myentity/10 it works. Unfortunately the rest call is initiated by a CDC-Test-Framework (pact) so I cannot change the requested path (containing the context path /testctx). So is there a way to tell spring boot test to start the rest endpoint with a defined context path also during testing?
You could try:
#WebMvcTest(controllers = {MyController.class})
#TestPropertySource(locations="classpath:application.properties")
class MyControllerTest {
#Autowired
protected MockMvc mockMvc;
#Value("${server.servlet.context-path}")
private String contextPath;
#BeforeEach
void setUp() {
assertThat(contextPath).isNotBlank();
((MockServletContext) mockMvc.getDispatcherServlet().getServletContext()).setContextPath(contextPath);
}
protected MockHttpServletRequestBuilder createGetRequest(String request) {
return get(contextPath + request).contextPath(contextPath)...
}

How to initialize web-service

I'm trying to create a simple web-page, which is displaying the data, received from web-service
#Service
#Transactional
public class TopicService {
#Autowired
private TopicRepository topicRepository;
public int saveTopic(Topic topic){
return topicRepository.save(topic).getId();
}
public Iterable<Topic> findAllTopics(){
return topicRepository.findAll();
}
public Topic findTopicByID(Long id){
return topicRepository.findOne(id);
}
public List<Topic> findTopicsByTag(Tag tag){
return topicRepository.findAllByTopicTag(tag);
}
}
topic repository extends CRUD-repository
#Repository
public interface TopicRepository extends CrudRepository<Topic, Long>
{
List<Topic> findAllByTopicTag(Tag currentTag);
}
the controller invokes a service in the following way
#Controller
public class HomeController {
private TopicService service;
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model)
{
Iterable<Topic> listTopic = service.findAllTopics();
return new ModelAndView("home", "model", listTopic);
}
}
this string
Iterable listTopic = service.findAllTopics();
throws null-pointer exception. I guess, because the service isn't initialized. How could I perform correct initialization of the service?
You need to autowire the TopicService in HomeController:
#Controller
public class HomeController {
#Aurowired
private TopicService service;

Categories

Resources