#RepositoryRestController not recognized - spring

I have the following controller:
#RepositoryRestController
public class TestController {
#RequestMapping(value = "/testables", method = RequestMethod.GET)
public String get(){
return "testin it";
}
}
And it is not picked up by Spring. I get 404 when I hit /apiroot/testables address. If I change it to be #RestController and add the api root to request mapping value, then it works. Also, if I change the mapping to point to "/orders/testables", it works as #RepositoryRestController. I also have the following controller in the same package which works fine:
#RepositoryRestController
public class SendEmailController {
#Autowired
private MessageSource messageSource;
#Autowired
private JavaMailSender javaMailSender;
#Autowired
private OrderRepository orderRepository;
#RequestMapping(value = "/orders/{id}/sendOrderEmailToSupplier")
public void sendOrderEmailToSupplier(#PathVariable("id") Long id, WebRequest request) throws MessagingException {...

#RepositoryRestController deal with basePath only for resources which it manage. In other cases the path should not contain the "base".
If you want to build custom operations underneath basePath, you can use #BasePathAwareController.

Related

rest controller for Spring Data REST repository

I have implemented a simple Spring Data REST repository which works as expected and I am fine with it exposing all methods. This is what it looks like:
#RepositoryRestResource(path = "employees")
public interface EmployeeRepository extends PagingAndSortingRepository<Employees, Integer>
{ }
Now I would like to wrap this repository in a controller, so I can later add Hystrix to it for fallbacks and exception handling. My issue is now, that I would like to keep the behavior of the repository above and just pass the response through the controller to the client. Is there a possible way without reimplementing all the methods of my repository (including sorting and pagination)?
This is what my controller currently looks like:
#RepositoryRestController
public class EmployeeController {
private final EmployeeRepository repository;
#Autowired
public EmployeeController(EmployeeRepository repo) {
repository = repo;
}
// Here I would like to return the same respone as my repository does
#RequestMapping(method = GET, value = "/employees")
public #ResponseBody ResponseEntity<?> parseRequest() {
return ResponseEntity.ok("hi");
}
}
It seems that you could simply call the method from your repository. Did you try it?
#RepositoryRestController
public class EmployeeController {
private final EmployeeRepository repository;
#Autowired
public EmployeeController(EmployeeRepository repository) {
this.repository = repository;
}
#RequestMapping(method = GET, value = "/employees")
public #ResponseBody ResponseEntity<List<Employee>> parseRequest() {
List<Employee> employees = repository.getEmployees();
return new ResponseEntity(employees, HttpStatus.OK);
}
}

how to create RestController in test directory for SpringBoot Application

Im currently writing integration test for SpringBoot Application .
It's functionality is to receive/send request from outside and forward/receive them to another application(APP_2). So there are two systems which needs to be mocked outside System and APP_2 .
HomeController
#Controller
public class HomeController {
#Autowired
ForwardController forwardController;
#RequestMapping("/")
public #ResponseBody
String greeting() {
return forwardController.processGET().toString();
}
}
ForwardController
#Service
public class ForwardController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment environment;
private ResponseEntity sendRequest(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables) {
return restTemplate.exchange( url, method, requestEntity, responseType,uriVariables);
}
public ResponseEntity processGET()
{
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<?> entity = new HttpEntity<>(headers);
String app_2_url = environment.getProperty(Constants.APP_2_URL);
ResponseEntity<String> response = sendRequest(app_2_url,HttpMethod.GET,entity,String.class);
return response;
}
}
APP_2_CONTROLLER
#Controller
public class App_2_Controller {
#RequestMapping("/app2Stub")
public #ResponseBody
String greeting() {
return "Hello End of world";
}
}
Test Class which simulates the external request behavior to the system:
HTTP_request_Test
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Application.class)
public class HttpRequestTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private Environment environment;
#Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello End of world");
}
}
Here in this test class I'm overriding the properties by having two property file. So when we run test the request would be sent to App_2_Controller ( Mock in my project ) rather than the real App .
QUESTION :
Is there any way to have the APP_2_CONTROLLER inside the test folder ? This is because I don't want to expose the unwanted test endpoint in my Actual application .
Here in the above project , Im changing the URL with properties. Is there a better way to put a controller for the same URL. For simplicity sake lets assume, app_2 url is app.com:9000/serve
Spring already comes with a MockRestServiceServer, that makes this a lot easier so that you don't have to create your own dummy controllers (App_2_Controller). So in your case, you can remove that controller, and write a test like this for ForwardController:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class ForwardControllerTest {
#Autowired
private RestTemplate restTemplate;
#Autowired
private ForwardController forwardController; // Your service
private MockRestServiceServer server;
#Before
public void setUp() {
server = MockRestServiceServer.bindTo(restTemplate).build();
}
#Test
public void processGet_returnsResponseFromAPI() {
server.expect(once(), requestTo("http://app.com:9000/serve"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("Hello End of world", MediaType.TEXT_PLAIN));
assertThat(forwardController.processGET().getBody()).isEqualTo("Hello End of world"));
}
}
Additionally, you can create a separate test for your actual controller (ForwardController is just a service), mock ForwardController and use MockMvc:
#RunWith(SpringRunner.class)
#WebMvcTest
public class HomeControllerTest {
#Autowired
private HomeController homeController;
#Autowired
private MockMvc mockMvc;
#MockBean
private ForwardController forwardController;
#Test
public void greeting_usesForwardController() {
when(forwardController.expectGET()).thenReturn("Hello End of world");
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello End of world")));
}
}
In this case, you'll end up with two tests:
One test to verify that RestTemplate is used to capture the proper response from your external REST API.
Another test to verify that HomeController just forwards whatever ForwardController responds.

Spring MVC dynamically configured getting 404 but works with MockMVC

I have the following controller (notice how it's configured):
#Controller
public class MyController {
#Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
#PostConstruct
private void createRequestMapping() throws Exception {
RequestMappingInfo commandExecutionRequestMappingInfo =
RequestMappingInfo
.paths(endpointUri)
.methods(RequestMethod.POST)
.consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.build();
requestMappingHandlerMapping
.registerMapping(commandExecutionRequestMappingInfo, this,
MyController.class.getDeclaredMethod("execute", String.class));
RequestMappingInfo getAvailableCommandsRequestMappingInfo =
RequestMappingInfo
.paths(endpointUri)
.methods(RequestMethod.GET)
.build();
requestMappingHandlerMapping
.registerMapping(getAvailableCommandsRequestMappingInfo, this,
MyController.class
.getDeclaredMethod("getAvailableCommands", Model.class));
}
#ResponseBody
private String execute(#RequestBody String command) {
...
}
private String getAvailableCommands(Model model) {
model.addAttribute("docs", handlerDocs);
return "docs";
}
There is a docs.html page which sits in main/resources/templates/docs.html.
When I run a test with MockMvc, it see the output of that page:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class ApplicationConfig {
}
Here is the test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ApplicationConfig.class)
#AutoConfigureMockMvc
public class SpringRestCommanderTest {
#Autowired
private MockMvc mvc;
#Test
public void testGetAvailableCommands() throws Exception {
mvc.perform(get("/execute")).andDo(print());
}
}
However, when I run this same thing in a real app, it gives me a 404 for the GET request when I try it in the browser.
What's strange is that the SAME /execute works when issue a POST request to it. As you can see above, the dynamic mapping is very similar between POST and GET.
What am I doing wrong here?

Spring MVC - Autowired field from header

I made web service with spring mvc(version 4).
This service used token in http header for authorization.
I want to value in http header bind to field in model class auto.
Is it possible? How can I do?
(See below code and comment)
Controller
#Controller
#RequestMapping(value = "/order")
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
#Autowired
private OrderService orderService;
#RequestMapping(value = "/")
#ResponseBody
public List<Order> getAll() throws Exception {
// I want to remove two line below with auto binding (userToken field in model)
// in all controller using token value
String token = request.getHeader("X-Auth-Token"); // remove~
orderService.setUserToken(token); // remove~
orderService.getAllbyUser()
return items;
}
}
Model(Service)
#Service
public class OrderService {
//#Autowired - is it possible?
private String userToken;
public String setUserToken(String userToken)
{
this.userToken = userToken;
}
public List<Order> getAllbyUser() {
String userId = userMapper.getUserId(userToken);
List<Order> list = orderMapper.getAllbyUser(userId);
return list;
}
}
#Autowire is for Spring to inject beans one to another. If you want to inject a String to a bean you can with the org.springframework.beans.factory.annotation.Value annotation.
For example:
#Value("${user.token}")
private String userToken;
This will make Spring search of the user.token in the VM args and other places (which I don't remember and in some specific order).
But again, as said in my initial comment, from the code you show here it seems to be an error setting this field as it is context specific and the #Service (by default) indicates that the OrderService is a singleton.
In order to read a header value from request, you can use #RequestHeader("X-Auth-Token") in your controller, as shown below:
#RequestMapping(value = "/")
#ResponseBody
public List<Order> getAll(#RequestHeader("X-Auth-Token") String token) throws Exception {
orderService.setUserToken(token); // remove~
orderService.getAllbyUser()
return items;
}
Hope this helps you.

Testing #ModelAttribute method on a controller

This are the annotated methods in the controller:
#RequestMapping(method = RequestMethod.GET)
public String getClient(#PathVariable("contractUuid") UUID contractUuid, Model model) {
ClientDto clientDto = new ClientDto();
clientDto.setContractUuid(contractUuid);
model.addAttribute("client", clientDto);
return "addClient";
}
#ModelAttribute("contract")
public ContractDto getContract(#PathVariable("contractUuid") UUID contractUuid) throws ContractNotFoundException {
return contractService.fromEntity(contractService.findByUuid(contractUuid));
}
The test method that I am trying is shown below, but it fails for attribute contract. The attribute client is added to the Model in a #RequestMapping method.
private MockMvc mockMvc;
#Autowired
private ContractService contractServiceMock;
#Autowired
private ClientService clientServiceMock;
#Autowired
protected WebApplicationContext wac;
#Before
public void setup() {
Mockito.reset(contractServiceMock);
Mockito.reset(clientServiceMock);
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void test() throws Exception {
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
mockMvc.perform(get("/addClient/{contractUuid}", uuid))
.andExpect(status().isOk())
.andExpect(view().name("addClient"))
.andExpect(forwardedUrl("/WEB-INF/pages/addClient.jsp"))
.andExpect(model().attributeExists("client"))
.andExpect(model().attributeExists("contract"));
}
The contract attribute shows in the jsp page when I run the application, since I use some of its attributes, but since it fails in the test method is there another way to test it ?
It fails with the message:
java.lang.AssertionError: Model attribute 'contract' does not exist
Spring is 4.0.1.RELEASE
It seems it was my fault.
Even though the #ModelAttribute method returns an instance of ContractDto I only mocked one method used from the service:
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
and so findByUuid returned something, but contractService.fromEntity was left untouched so I had to also mock it:
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
ContractDto contractDto = new ContractDto(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
when(contractServiceMock.fromEntity(contract)).thenReturn(contractDto);

Resources