Specifically, I want whole controlerer to send "Access-Control-Allow-Origin=*".
I've tried below but that doesn't seem to work:
#Controller
#RequestMapping(value = "/controller/base", headers="Access-Control-Allow-Origin=*")
public class UserController {
#RequestMapping(method = RequestMethod.GET, value = "/blah", produces = application/json")
#ResponseBody
public Map blah(/*...,*/ HttpServletResponse response, HttpServletRequest request) throws Exception {
//...
}
}
It only work when I explicitly set (in all methods) like that:
response.addHeader("Access-Control-Allow-Origin", "*");
Any way to somehow set this for one or more controllers in a single place?
As far as I know there is no way to intercept all methods of a controller from the controller itself (except for ugly hacks such as using #InitBinder).
However, you can create a HandlerInterceptor and apply it to the base URL of your controller. For example, using #EnableMvc:
#Configuration
#EnableWebMvc
public class MyWebConfiguration extends WebMvcConfigurerAdapter {
...
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(...).addPathPatterns("/controller/base/**");
}
...
}
Consider using the #ControllerAdvice annotation introduced in Spring 3.2, combined with #InitBinder.
"Classes annotated with #ControllerAdvice can contain #ExceptionHandler, #InitBinder, and #ModelAttribute methods and those will apply to #RequestMapping methods across controller hierarchies as opposed to the controller hierarchy within which they are declared. #ControllerAdvice is a component annotation allowing implementation classes to be auto-detected through classpath scanning."
Set the response header in the method annotated with #InitBinder.
Annotate the class with #ControllerAdvice to have it process the #InitBinder globally.
Related
Is it possible to Define a Spring RestController (#RestController annotated class) solely in the Java Configuration (the class with #Configuration annotated in the method marked with #Bean)?
I have an application managed by spring boot (the version doesn't matter for the sake of the question, even the last one available). This application exposes some endpoints with REST, so there are several rest controllers, which in turn call the services (as usual).
Now depending on configuration (property in application.yml) I would like to avoid starting some services and, say 2 classes annotated with #RestController annotation because they deal with the "feature X" that I want to exclude.
I would like to configure all my beans via Java configuration, and this is a requirement. So my initial approach was to define all the beans (controllers and services) in a separate configuration which is found by spring boot during the scanning) and put a #ConditionalOnProperty on the configuration so that it will appear in one place:
#Configuration
public class MyAppGeneralConfiguration {
// here I define all the beans that are not relevant for "feature X"
#Bean
public ServiceA serviceA() {}
...
}
#Configuration
#ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true")
public class MyAppFeatureXConfiguration {
// here I will define all the beans relevant for feature X:
#Bean
public ServiceForFeatureX1 serviceForFeatureX1() {}
#Bean
public ServiceForFeatureX2 serviceForFeatureX2() {}
}
With this approach My services do not have any spring annotations at all and I don't use #Autowired annotation as everything is injected via the constructors in #Configuration class:
// no #Service / #Component annotation
public class ServiceForFeatureX1 {}
Now my question is about the classes annotated with #RestContoller annotation. Say I have 2 Controllers like this:
#RestController
public class FeatureXRestController1 {
...
}
#RestController
public class FeatureXRestController2 {
...
}
Ideally I would like to define them in the Java Configuration as well, so that these two controllers won't even load when I disable the feature:
#ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true", matchIfMissing=true)
public class MyAppFeatureXConfiguration {
#Bean
#RestController // this doesn't work because the #RestController has target Type and can't be applied
// to methods
public FeatureXRestController1 featureXRestController1() {
}
So the question is basically is it possible to do that?
RestController is a Controller which is in turn a component hence its subject to component scanning. Hence if the feature X is disabled the rest controllers for feature X will still start loading and fail
because there won't be no "services" - beans excluded in the configuration, so spring boot won't be able to inject.
One way I thought about is to define a special annotation like #FeatureXRestController and make it #RestController and put #ConditionalOnProperty there but its still two places and its the best solution I could come up with:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
#ConditionalOnProperty(name = "myapp.featureX.enabled", havingValue = "true", matchIfMissing=true)
public #interface FeatureXRestController {
}
...
#FeatureXRestController
public class FeatureXRestController1 {...}
#FeatureXRestController
public class FeatureXRestController2 {...}
I've Found a relatively elegant workaround that might be helpful for the community:
I don't use a specialized meta annotation like I suggested in the question and annotate the controller of Feature X with the regular #RestController annotation:
#RestController
public class FeatureXController {
...
}
The Spring boot application class can be "instructed" to not load RestControllers during the component scanning exclusion filter. For the sake of example in the answer I'll use the built-in annotation filter, but in general custom filters can be created for more sophisticated (real) cases:
// Note the annotation - component scanning process won't recognize classes annotated with RestController, so from now on all the rest controllers in the application must be defined in `#Configuration` classes.
#ComponentScan(excludeFilters = #Filter(RestController.class))
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Now, since I want the rest controller to be loaded only if Feature X is enabled, I create the corresponding method in the FeatureXConfiguration:
#Configuration
#ConditionalOnProperty(value = "mayapp.featureX.enabled", havingValue = "true", matchIfMissing = false)
public class FeatureXConfiguration {
#Bean
public FeatureXService featureXService () {
return new FeatureXService();
}
#Bean
public FeatureXRestController featureXRestController () {
return new FeatureXRestController(featureXService());
}
}
Although component scanning process doesn't load the rest controllers, the explicit bean definition "overrides" this behavior and the rest controller's bean definition is created during the startup. Then Spring MVC engine analyzes it and due to the presence of the #RestController annotation it exposes the corresponding end-point as it usually does.
You can use local classes in the java config. E.e.
#Configuration
public class MyAppFeatureXConfiguration {
#Bean
public FeatureXRestController1 featureXRestController1(AutowireCapableBeanFactory beanFactory) {
#RestController
class FeatureXRestController1Bean extends FeatureRestController1 {
}
FeatureXRestController1Bean featureBean = new FeatureXRestController1Bean();
// You don't need this line if you use constructor injection
autowireCapableBeanFactory.autowireBean(featureBean);
return featureBean;
}
}
Then you can omit the #RestController annotation on the "real" implementation, but use the other annotations like #RequestMapping as usual.
#RequestMapping(...)
public class FeatureXRestController1 {
#RequestMapping(value="/somePath/{someId}", method=RequestMethod.GET)
public String findSomething(#PathVariable String someId) {
...
}
}
Sine the FeatureXRestController1 doesn't have a #RestController annotation, it is not a #Component anymore and thus will not be picked up through component scan.
The MyAppFeatureXConfiguration returns a bean that is a #RestController. This FeatureXRestController1Bean extends the FeatureXRestController1 and thus has all the methods and request mappings of the superclass.
Since the FeatureXRestController1Bean is a local class it is not included in a component scan. This does the trick for me ;)
I like both the solutions presented above. However, I came up with another one which worked for me and is pretty clean.
So, I decided to create my beans only using #Configuration classes, and I gave up the #RestController annotations entirely. I put the #Configuration class for web controllers in a separate package, so that I can pass the class descriptor to the #ComponentScan or #ContextConfiguration annotations whenever I want to enable the creation of controller beans. The most important part is to add the #ResponseBody annotation to all controller classes, above the class name, to preserve the REST controller properties. Very clean.
The drawback is that the controller classes are not recognized by the #WebMvcTest annotation, and I need to create the beans for all my controllers every time I do a MockMvc test for one controller. As I have just four controllers though, I can live with it for now.
Although every page on internet says that #RestController is a specification of #Component.I dont know whether it has to be related with DispatcherServlet. But when I try below code by switching between #RestController & #Component, I don't see same behaviour :
First I tried with #RestController:
#RestComponent
public class TestController {
#RequestMapping(value="/testController", method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)
public void testController() {
System.out.println("Hello");
}
}
I got below output in Console:
Hello
Second I tried with #Component + #ResponseBody:
#Component
#ResponseBody
public class TestController {
#RequestMapping(value="/testController", method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)
public void testController() {
System.out.println("Hello");
}
}
I got an error on postman:
{
"timestamp": 1570998345860,
"status": 405,
"error": "Method Not Allowed",
"message": "Request method 'POST' not supported",
"path": "/testController"
}
If both annotations are same, then why is there a difference in output ??
Below is the Source code for #RestController & #Controller , which shows that both #RestController & #Controller are specification of #Component:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Controller
#ResponseBody
public #interface RestController {
}
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
}
Maybe it has to be related with DispatcherServlet. It might be possible that Dispatcher Servlet only check for URL in #RestController annotated classes.
#Controller is user in traditional controller and #RestController annotation was introduced to simplify the creation of RESTful web services. It's a convenience annotation that combines #Controller and #ResponseBody
#Controller annotation is simply a specialized of the #Component class and allows implementation classes to be autodetect through the classpath scanning.
#Controller is typically used in combination with a #RequestMapping annotation used on request handling methods.
The request handling method is annotated with #ResponseBody. This annotation enables automatic serialization of the return object into the HttpResponse.
#RestController is a specialized version of the controller. It includes the #Controller and #ResponseBody annotations and as a result, simplifies the controller implementation.
The controller is annotated with the #RestController annotation, therefore the #ResponseBody isn't required.
Every request handling method of the controller class automatically serializes return objects into HttpResponse.
see here in detail : https://www.baeldung.com/spring-controller-vs-restcontroller
Just because #RestController is a #Component does not mean you can achieve the same functionality by switching to the broader one #Component. Even with the addition of #ResponseBody, you don't achieve equivalent functionality (as expressed through the Request Method POST not being supported).
Replace #Component with #Controller, because a #RestController has the exact same functionality as a #Controller + #ResponseBody. You can also see this in the meta-annotations of #RestController, you see it is meta-annotated with #Controller instead of just #Component. In turn, #Controller is meta-annotated with #Component.
The #Controller , #RestController , #Service etc are all annotations that are meta-annotated with the #Component annotation. All these annotations are essentially specializations of the #Component annotation for specific use cases.
The #Component annotation is generally used to register a Spring bean.
The #Controller annotation even though it is annotated with #Component internally , it provides a different functionality altogether.By annotating a class with this annotation we are essentially telling spring to scan this class for #RequestMapping annotation to register beans for request mapping. This does not happen when you just use the #Component annotation.
When the spring application starts up, the DispatcherServlet will enable the RequestMappingHandlerMapping RequestMappingHandlerAdapter (which is a handler mapping which looks for #RequestMapping annotations on #Controllers). So when a request reaches the dispatcher servlet it gives it to the RequestMappingHandlerMapping which resolves the uri to a controller method bean.For more information read : DispatcherServlet, Updated Doc Spring 5.x HandlerMapping
Note :
In older versions of spring DefaultAnnotationHandlerMapping is enabled for this by spring.
So in summary :
#Component is a generic stereotype for any Spring-managed component
or bean.
#Repository is a stereotype for the persistence layer.
#Service is a stereotype for the service layer.
#Controller is a stereotype for the presentation layer (spring-MVC).
Spring [Doc][4].
The #Controller annotation indicates that a particular class serves
the role of a controller.
The basic purpose of the #Controller annotation is to act as a
stereotype for the annotated class, indicating its role. The
dispatcher will scan such annotated classes for mapped methods,
detecting #RequestMapping annotations (see the next section).
Annotated controller beans may be defined explicitly, using a standard
Spring bean definition in the dispatcher's context. However, the
#Controller stereotype also allows for autodetection, aligned with
Spring 2.5's general support for detecting component classes in the
classpath and auto-registering bean definitions for them.
Similar post : What's the difference between #Component, #Repository & #Service annotations in Spring?
You should not get the same behaviour because #Component is more generic. It only registers your class as a Spring Managed Bean. However, #RestController not only registers your class as a managed bean, it further registers it as an entry point for HTTP calls to the specified URL path. Same goes for other Layer specific annotations like #Repository, #Configuration e.t.c
I know there is a question about Difference between spring #Controller and #RestController, also I know the difference about the two annotations.
My question is regarding interceptors only, is there any difference between the two annotations when we define interceptors.
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TemplateMappingInterceptor()).addPathPatterns("/**", "/admin-functions**").excludePathPatterns("/login**");
}
}
#RestController is a newer annotation introduced to simplify building of rest controllers. It includes the #Controller and #ResponseBody annotations.
In a wide point of view, they both do the same thing in the end, one in a simpler way. Interceptors operate on a higher level and are independent of the inner workings of a rest controller, so nope, there is no difference.
Difference between spring #Controller and #RestController annotation.
Can #Controller annotation be used for both Web MVC and REST applications?
If yes, how can we differentiate if it is Web MVC or REST application.
#Controller is used to mark classes as Spring MVC Controller.
#RestController is a convenience annotation that does nothing more than adding the #Controller and #ResponseBody annotations (see: Javadoc)
So the following two controller definitions should do the same
#Controller
#ResponseBody
public class MyController { }
#RestController
public class MyRestController { }
In the code below I'll show you the difference
between #controller
#Controller
public class RestClassName{
#RequestMapping(value={"/uri"})
#ResponseBody
public ObjectResponse functionRestName(){
//...
return instance;
}
}
and #RestController
#RestController
public class RestClassName{
#RequestMapping(value={"/uri"})
public ObjectResponse functionRestName(){
//...
return instance;
}
}
the #ResponseBody is activated by default. You don't need to add it above the function signature.
#RestController is the combination of #Controller and #ResponseBody.
Flow of request in a #Controller class without using a #ResponseBody annotation:
#RestController returns an object as response instead of view.
If you use #RestController you cannot return a view (By using Viewresolver in Spring/springboot) and yes #ResponseBody is not needed in this case.
If you use #Controller you can return a view in Spring web MVC.
#RestController annotated classes are the same as #Controller but the #ResponseBody on the handler methods are implied.
Actually, be careful - they are not exactly the same.
If you define any interceptors within your application, they will not apply to Controllers annotated as #RestController, however they do work with #Controller annotated controllers.
ie. configuration for the interceptor:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TemplateMappingInterceptor()).addPathPatterns("/**", "/admin-functions**").excludePathPatterns("/login**");
}
}
and in the declaration of a Spring controller:
#Controller
public class AdminServiceController {...
Will work, however
#RestController
public class AdminServiceController {...
does not end up having the interceptor being associated with it.
#Controller returns View. #RestController returns ResponseBody.
As you can see in Spring documentation (Spring RestController Documentation) Rest Controller annotation is the same as Controller annotation, but assuming that #ResponseBody is active by default, so all the Java objects are serialized to JSON representation in the response body.
THE new #RestController annotation in Spring4+, which marks the class as a controller where every method returns a domain object instead of a view. It’s shorthand for #Controller and #ResponseBody rolled together.
#RestController was provided since Spring 4.0.1. These controllers indicate that here #RequestMapping methods assume #ResponseBody semantics by default.
In earlier versions the similar functionality could be achieved by using below:
#RequestMapping coupled with #ResponseBody like #RequestMapping(value = "/abc", method = RequestMethod.GET, produces ="application/xml")
public #ResponseBody MyBean fetch(){
return new MyBean("hi")
}
<mvc:annotation-driven/> may be used as one of the ways for using JSON with Jackson or xml.
MyBean can be defined like
#XmlRootElement(name = "MyBean")
#XmlType(propOrder = {"field2", "field1"})
public class MyBean{
field1
field2 ..
//getter, setter
}
#ResponseBody is treated as the view here among MVC and it is dispatched directly instead being dispatched from Dispatcher Servlet and the respective converters convert the response in the related format like text/html, application/xml, application/json .
However, the Restcontroller is already coupled with ResponseBody and the respective converters. Secondly, here, since instead of converting the responsebody, it is automatically converted to http response.
#Controller is used in legacy systems which use JSPs. it can return views.
#RestController is to mark the controller is providing REST services with JSON response type. so it wraps #Controller and #ResponseBody annotations together.
#Controller: This annotation is just a specialized version of #Component and it allows the controller classes to be auto-detected based on classpath scanning.
#RestController: This annotation is a specialized version of #Controller which adds #Controller and #ResponseBody annotation automatically so we do not have to add #ResponseBody to our mapping methods.
The #Controller annotation indicates that the class is a "Controller" like a web controller while #RestController annotation indicates that the class is a controller where #RequestMapping methods assume #ResponseBody semantics by default i.e. servicing REST API
#RestController is composition of #Controller and #ResponseBody, if we are not using the #ResponseBody in Method signature then we need to use the #Restcontroller.
Instead of using #Controller and #ResponseBody, #RestController let's you expose Rest APIs in Spring 4.0 and above.
I'm using the Spring MVC framework. In the code fragment below, is it possible for me to have Spring inject a bean into the MyRequestClass request parameter when it is constructed (ie. using #Autowired or ApplicationContextAware interface)? I need that request object to access one of my singleton Spring beans and would rather not have to create an ApplicationContextAware implementing singleton just for this purpose.
So far, I'm just getting NullPointerExceptions trying to access the beanaroo property with either approach.
#RequestMapping("/**")
#Controller
public class MyController {
#RequestMapping(value = "/mymethod", method = RequestMethod.POST)
public ModelAndView myMethod(#RequestBody MyRequestClass request,
ModelMap modelMap,
HttpServletResponse response) {
...
}
}
eg.
public class MyRequestClass {
#Autowired
private MyInjectedBean beanaroo;
}
I also tried defining a prototype bean of that class in my application context file but it didn't have any effect either.
You can, using #Configurable and aspectJ, but I wouldn't if I were you.
I'd just have the bean have a method that takes MyInjectedBean as param, and call that from the controller.