Creating controllers hierarchy with #RestController (spring boot application) - spring-boot

I want to create an hierarchy in my spring boot application controllers. Meaning: when requesting a path in one controller he will return the next controller.
It doesn't work...
In the example I created I expected to answer url:
/v1/notifications/test - should work
/v1 - shouldn't work
/v1/notifications - shouldn't work
/test - shouldn't work
etc...
This is what I did so far:
Controller root:
#RestController
#RequestMapping("/")
public class BaseController {
#Autowired
BaseControllerV1 baseControllerV1;
#RequestMapping(value = "/v1")
public BaseControllerV1 getBaseControllerV1(){
return baseControllerV1;
}
}
Controller child:
#RestController
public class BaseControllerV1 {
#Autowired
NotificationsControllerV1 notificationsControllerV1;
#RequestMapping(value = "/notifications")
public NotificationsControllerV1 getNotificationsControllerV1() {
return notificationsControllerV1;
}
}
Controller grandchild:
#RestController
public class NotificationsControllerV1 {
#RequestMapping(value = "/test",
method = RequestMethod.GET,
produces = "application/json")
public String getTest(){
return "{test: ok}";
}
}

Related

It seems `#GetMapping` doesn't work with `#Controller`, why is that?

Per the doc, #RestController is just a convenience annotation which combines #Controller and #ResponseBody and #GetMapping is a composed annotation that acts as a shortcut for #RequestMapping(method = RequestMethod.GET), which means #GetMapping should work well with both #RestController and #Controller
In fact #GetMapping only works with #RestController.
#RestController
public class HelloController {
#GetMapping("/")
public String hello(){
return "hello";
}
}
while
#Controller
public class HelloController {
#GetMapping("/")
public String hello(){
return "hello";
}
}
doesn't work.
I know I can use #RequestMapping(value = "/", method = RequestMethod.GET) instead, I'd just like to know why. Could someone give a clue?
#Controller
public class TestController {
#GetMapping("/")
#ResponseBody
public String hello() {
return "hello";
}
}
Add the #ResponceBody annotation then, it will work.
The #Controller annotation indicates that the class is a “Controller”
e.g. a web controller
The #RestController annotation indicates
that the class is a controller where #RequestMapping methods assume
#ResponseBody semantics by default i.e. servicing REST API.

Springboot evaluate api path from Controller method name

#RestController
#RequestMapping(value = "/api/orders/")
public class OrderController {
#PostMapping("create")
public ResponseEntity<OrderResponseV2> create(
#RequestBody OrderRequestV2 orderRequest) {
OrderResponse response = createOrderService.createOrder(orderRequest);
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
}
Is there a way to get the whole API path including root context during runtime using Relfection from class+method name?

How to implement REST API on a RestController using ModelAndView

Im struggling on how to implement a API when the methods in my controller are returning a ModelAndView. Most tutorials I can find are returning ResponseEntities. Should i be making seperate API controllers which strictly handle the API calls under a /api mapping? (which i believe isn't RESTFUL practice). Or is it possible to handle my API calls in the same controller, even when making use of ModelAndView?
My controller looks as following:
#RestController
#RequestMapping("/dish")
public class DishController {
private final DishRepository dishRepository;
public DishController(DishRepository dishRepository) {
this.dishRepository = dishRepository;
}
#GetMapping
public ModelAndView list() {
Iterable<Dish> dishes = this.dishRepository.findAll();
return new ModelAndView("dishes/list", "dishes", dishes);
}
#GetMapping("{id}")
public ModelAndView view(#PathVariable("id") Dish dish) {
return new ModelAndView("dishes/view", "dish", dish);
}
#GetMapping(params = "form")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String createForm(#ModelAttribute Dish dish) {
return "dishes/form";
}
#ResponseStatus(HttpStatus.CREATED)
#PostMapping
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView create(#Valid Dish dish, BindingResult result,
RedirectAttributes redirect) {
if (result.hasErrors()) {
return new ModelAndView("dishes/form", "formErrors", result.getAllErrors());
}
dish = this.dishRepository.save(dish);
redirect.addFlashAttribute("globalMessage", "view.success");
return new ModelAndView("redirect:/d/{dish.id}", "dish.id", dish.getId());
}
#RequestMapping("foo")
public String foo() {
throw new RuntimeException("Expected exception in controller");
}
#ResponseStatus(HttpStatus.OK)
#GetMapping("delete/{id}")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView delete(#PathVariable("id") Long id) {
this.dishRepository.deleteById(id);
Iterable<Dish> dishes = this.dishRepository.findAll();
return new ModelAndView("dishes/list", "dishes", dishes);
}
#ResponseStatus(HttpStatus.OK)
#GetMapping("/modify/{id}")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ModelAndView modifyForm(#PathVariable("id") Dish dish) {
return new ModelAndView("dishes/form", "dish", dish);
}
You should not use Model and View in RestController. The main goal of RestController is to return data, not views. Take a look here for more details: Returning view from Spring MVC #RestController.
#RestController is a shorthand for writing #Controller and #ResponseBody, which you should only use if all methods are returning an object that should be treated as the response body (eg. JSON).
If you want to combine both REST endpoints and MVC endpoints within the same controller, you can annotate it with #Controller and individually annotate each method with #ResponseBody.
For example:
#Controller // Use #Controller in stead of #RestController
#RequestMapping("/dish")
public class DishController {
#GetMapping("/list")
public ModelAndView list() { /* ... */ }
#GetMapping
#ResponseBody // Use #ResponseBody for REST API methods
public List<Dish> findAll() { /* ... */ }
}
Alternatively, as you've mentioned, you can use multiple controllers:
#Controller
#RequestMapping("/dish")
public class DishViewController { /* ... */ }
#RestController
#RequestMapping("/api/dish")
public class DishAPIController { /* ... */ }

DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler

I am trying to design a rest api, and below is my controller code.
when i invoke http://localhost:8080/ the response is fine, but if i hit http://localhost:8080/api/ca it thorws javax.servlet.ServletException: No adapter for handler [...CaDetailController#48224381]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
#RestController("/api")
public class CaDetailController {
private static final Logger logger = LoggerFactory.getLogger(GetClassLoader.class.getClass());
#Autowired
CaService caService;
#RequestMapping(path = "/ca", method = RequestMethod.GET)
public #ResponseBody List<CaDetail> getCorporateActions() {
logger.info("CaDetailController.findAllCaDetails()");
return caService.findAllCaDetails();
}
#RequestMapping(path = "/ca/{caId}", method = RequestMethod.GET)
public #ResponseBody List<CaDetail> getCorporateActions(#PathParam("caId") long caId) {
logger.info("CaDetailController.getCorporateActions() : caId : " + caId);
return caService.findAllCaDetails();
}
}
Updated controller.
#RestController
#RequestMapping("/api/ca")
public class CaDetailController {
private static final Logger logger = LoggerFactory.getLogger(GetClassLoader.class.getClass());
#Autowired
CaService caService;
#GetMapping(path = "/")
public #ResponseBody List<CaDetail> getCorporateActions() {
logger.info("CaDetailController.findAllCaDetails()");
return caService.findAllCaDetails();
}
#GetMapping(path = "/{caId}")
public #ResponseBody List<CaDetail> getCorporateActions(#PathParam("caId") Long caId) {
logger.info("CaDetailController.getCorporateActions() : caId : " + caId);
return caService.findAllCaDetails();
}
}
For clarity, fix is:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/ca")
public class CaDetailController {
#GetMapping
public String healthcheck1() {
return "ok!";
}
#GetMapping(path = "health")
public String healthcheck2() {
return "ok again!";
}
}
You can call this endpoints using URL:
http://localhost:8080/api/ca and
http://localhost:8080/api/ca/health
(Assuming default Spring Boot Tomcat configuration).
Don't add ("/api") value to #RestController Annotation,
add it to #RequestMapping
#RestController
#RequestMapping("api/")
...
Try this
#RestController
#RequestMapping("/api")
public class CaDetailController {
instead of
#RestController("/api")
public class CaDetailController {

Spring Boot Controller is not calling

My Application class in au.com.domain.demo package and my controller class au.com.domain.demo.control package. I tried to call get method in controller class, but it is not calling.
#SpringBootApplication
public class IssueTrackerApplication {
public static void main(final String[] args) {
System.out.println("coming here.0000..");
SpringApplication.run(IssueTrackerApplication.class, args);
}
}
My controller class is:
#RestController
public class IssueTrackingController {
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return "hello";
}
}
I suggest to try to follow an official tutorial from Spring: https://spring.io/guides/gs/rest-service/ - maybe it helps.
The other thing is - try not to return String from the method, but return an object instead. Spring Boot converts POJO classes to JSON.
Hope this helps :)

Resources