I am practicing spring boot OAuth2. I have and authorization server, resource server, and client server. I managed to secure an endpoint (/products).
I want show a list of products using Thymeleaf.
Here is the controller in the resource server:
#RestController
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/products")
public List<Product> getProducts(final Model model) {
return (List<Product>) productRepository.findAll();
}
}
Here is the controller in the client server:
#RestController
public class ArticlesController {
#Autowired
private WebClient webClient;
#GetMapping(value = "/articles")
public String[] getArticles(
#RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient) {
return this.webClient.get().uri("http://localhost:8090/articles")
.attributes(oauth2AuthorizedClient(authorizedClient)).retrieve().bodyToMono(String[].class).block();
}
#GetMapping(value = "/products")
public List<Product> getProducts(
#RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient) {
return this.webClient.get().uri("http://localhost:8090/products")
.attributes(oauth2AuthorizedClient(authorizedClient)).accept(MediaType.ALL).retrieve()
.bodyToFlux(Product.class).collectList().block();
}
}
With the code above I have a list of JSON Products. What is the best way to show this list as a table with Thymeleaf using the webClient?
I have created a small application using the spring boot framework. I have created a Rest Controler class.
and deploy it on tomcat, but I am getting 404 error i.e
The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
#RestController
#RequestMapping("students")
public class StudentController {
#Autowired
StudentRepository repository;
#GetMapping
public List<Student> getAllStudents() {
return (List<Student>) repository.findAll();
}
#PostMapping
public String createStudent() {
return "created";
}
#PutMapping
public String updateStudent() {
return "updated";
}
#DeleteMapping
public String deleteStudent() {
return "deleted";
}
}
You are missing slash in annotation, it should look like this
#RestController
#RequestMapping("/students")
public class StudentController {
...
}
#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?
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 { /* ... */ }
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}";
}
}