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

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.

Related

Spring Boot - #RestController annotation gets picked up, but when replaced with #Controller annotation, it stops working

Here is a dummy project for the same :-
BlogApplication.java
#SpringBootApplication
#RestController
public class BlogApplication {
public static void main(String[] args) {
SpringApplication.run(BlogApplication.class, args);
}
#GetMapping("/hello")
public String hello(#RequestParam(value = "name", defaultValue = "World") String name) {
return String.format("Hello %s!", name);
}
}
HtmlController.java
#Controller //does not work
OR
#RestController //works
public class HtmlController {
#GetMapping("/getHtmlFile")
public String getHtmlFile() {
return "Bit Torrent Brief";
}
}
Why is it that #RestController is able to map getHtmlFile but #Controller returns 404 when /getHtmlFile is hit?
#RestController is the combination of #Controller and #ResponseBody.
when you use #Controller, you should write #ResponseBody before your method return type
#Controller
public class HtmlController {
#GetMapping("/getHtmlFile")
public #ResponseBody String getHtmlFile() {
return "Bit Torrent Brief";
}
}
This works.

#Valid working with #ModelAttribute, not with #RequestAttribute

I'm implementing a #RestController and I realized that #Valid is working with #RequestBody or #ModelAttribute params of a #GetMapping method, but not with a #RequestAttribute parameter.
To get validated the #RequestAttribute annotated param I have to annotate my Controller class with #Validated.
Following my code:
Controller
#Log4j2
#RestController
#RequestMapping("/test/api/v1/entity")
public class MyController extends SomeController {
#GetMapping("/getInfo")
public ResponseEntity<<MyResponse>> infoStatus (RequestParam(required = false) String inputStr,
#Valid #RequestAttribute ObjectToValidate objToValidate){
//Any stuff here
}
}
Bean to validate
#Getter
#Setter
#Valid
public class ObjectToValidate {
#NotNull
#NotEmpty
private String anyCode;
}
The result is anyCode is not checked to be not null nor empty.
If I annotate MyController with #Validate, the ObjectToValidate param is validate as expected.
If I change controller as follows, the validation also works.
#Log4j2
#RestController
#RequestMapping("/test/api/v1/entity")
public class MyController extends SomeController {
#ModelAttribute
public ObjectToValidate addToModel(#RequestAttribute ObjectToValidate
objToValidate) { return objToValidate; }
#GetMapping("/getInfo")
public ResponseEntity<MyResponse> infoStatus (
#RequestParam(required = false) String inputStr,
#Valid #ModelAttribute ObjectToValidate objToValidate
){
//Any stuff here
}
}
Please, could you explain why?
#Valid can be used on #RequestBody Controller Method Arguments.
That is #RequestBody method argument can be annotated with #Valid to invoke automatic validation.
It will be no use if you annotate ObjectToValidate class with #Valid.
#PostMapping("/notes")
Note getNote(#Valid #RequestBody Note note) {
return repository.save(note);
}
To validate the path variable, the controller class should be annotated with #Validated
#RestController
#Validated // class level
public class NoteController {
#GetMapping("/note/{id}")
Note findOne(#PathVariable #NotBlank(message = "Id must not be empty") String id) {
return repository.findById(id)
.orElseThrow(() -> new NotekNotFoundException(id));
}
}
Hope it helps!!

#RepositoryRestController not recognized

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.

Abstract class and url mapping

I have an abstract class
I try to have all generic method in this class.
I get issue about mapping.
public abstract class BaseControllerNew<T extends BaseEntity, R extends BaseDto, S extends BaseSearch> {
...
#GetMapping(value = "/{id}")
public R getById(#PathVariable("id") Integer id){
return baseServiceNew.getById(id);
}
#GetMapping(value = "/")
public Page<R> get(Pageable page){
return baseServiceNew.get(page);
}
....
}
#RequestMapping(value = "/rest/vehicules")
#RestController
public class VehiculesRestController extends BaseControllerNew<Vehicules, VehiculesDto, VehiculesSearch>{
private VehiculesServiceImpl vehiculesService;
#Autowired
public VehiculesRestController(final VehiculesServiceImpl vehiculesService) {
super(vehiculesService);
this.vehiculesService = vehiculesService;
}
I'm able to call
/rest/vehicules/1
but i get 404 for
/rest/vehicules
The problem is with your additional "/", this means your URL will be "/rest/vehicules/"
You only need #GetMapping
#GetMapping
public Page<R> get(Pageable page){
return baseServiceNew.get(page);
}

Creating controllers hierarchy with #RestController (spring boot application)

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}";
}
}

Resources