How to configure springdoc ui without mapping the root path? - java

I'm trying to configure springdoc-openapi-ui in a spring web app (non-springboot). In my application I have a controller that is mapped to "/" which conflicts with the Springdoc SwaggerUiHome Controller. I get an error similar to this:
java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'swaggerUiHome' method 'org.springdoc.webmvc.ui.SwaggerUiHome#index() to {GET [/]}: There is already 'loginLogoutController' bean method com.company.MyController#method() mapped.
If I disable my controller which is mapped to '/' then the swagger UI comes up as expected. However, I need to have my controller running in order for my web app to work.
Even if I set 'springdoc.swagger-ui.use-root-path=false' (which should be the default anyway) it is still causing an ambiguous mapping error.
Is there any way around this? Any way to disable the default mapping of the SwaggerUiHome.index() to '/'?

SwaggerUiHome is only invoked if you define: springdoc.swagger-ui.use-root-path=true
To disable it: springdoc.swagger-ui.use-root-path=false
Note that default path of the swagger-ui is not the root path, but: http://server:port/context-path/swagger-ui.html

I'm getting this error if using regular spring framework (not using spring-boot). After dig into the source /springdoc-openapi-starter-webmvc-ui-2.0.2.jar/org.springdoc.webmvc.ui/SwaggerConfig.class
#Bean
#ConditionalOnMissingBean
#ConditionalOnProperty(name = SPRINGDOC_USE_ROOT_PATH, havingValue = "true")
#Lazy(false)
SwaggerUiHome swaggerUiHome() {
return new SwaggerUiHome();
}
Found that String SPRINGDOC_USE_ROOT_PATH = "springdoc.swagger-ui.use-root-path". I guess there maybe a bug that this property is not working for springdoc.swagger-ui.use-root-path=false
Here is my working solution after reading https://www.baeldung.com/spring-requestmapping#4-ambiguous-mapping-error
#GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
public String index() {
return "home";
}

Related

Using Java and Spring Boot and my #Value annotation for my application-dev.properties is not being resolved?

This is my very first question so I apologize if it is not specific enough (please be gentle haha) and I have already gone through documentation, but I am new to the field and none of it helped.
I am making a simple project that uses Java 18 and Spring Boot to make a call to an external API call and I want to hide my API key that I use for obvious reasons.
As of right now, I have an application-dev.properties file in my resources directory with the following (I am making up the actual key for security) and I have application-dev.properties in my .gitignore file so it doesn't get committed:
api-key=someText
And I am trying to use that value in my controller like so:
#RestController
public class ImageController {
#Value("${api-key}")
private String API_KEY;
#RequestMapping(value = "/image")
public List<String> getImages(#RequestParam(defaultValue = "4") String request) {
String url = "https://api.nasa.gov/planetary/apod?" + API_KEY + request;
RestTemplate restTemplate = new RestTemplate();
The error I am receiving is this:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'imageController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'api-key' in value "${api-key}"
Thanks in advance!
As mentioned in the other answer,
the name of the application-dev.properties file requires that
you activate the "dev" profile or Spring Boot will never read it and the
values therein will not be available to your #Value annotation.
Here is a link to a Baeldung article that discusses Spring Boot profiles:
Spring Boot Profiles at Baeldung
(I am not associated with Baeldung,
I just like much of their stuff).
If you are not planning to use profiles in your application,
change the name of the properties file to "application.properties".
As you mentioned you are using application-dev.properties therefore you have to do profiling correctly. In your application.properties add spring.profiles.active=dev to activate dev profile.

Why am I getting a "requestMappingHandlerMapping" error when I have 2 controller methods which has same exact endpoint string value

I am trying to overload a #RestController class's method which has the same exact endpoint (url).
However, when I do, I get the following error:
Error creating bean with name 'requestMappingHandlerMapping' defined
in class path resource
[org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
Invocation of init method failed; nested exception is
java.lang.IllegalStateException: Ambiguous mapping. Cannot map
'helloWorldController' method
com.rest.webservices.restfulwebservices.helloworld.HelloWorldController#helloWorldInternationalized(Locale)
to {GET [/hello-world-internationalized]}: There is already
'helloWorldController' bean method
If I change the endpoint (url) to something else, error goes away. In the code below, you can see I use the "hello-world-internationalized" string as endpoint value for both methods which gives me an error about the bean method.
#RestController
public class HelloWorldController {
#Autowired
private MessageSource messageSource;
#GetMapping(path="/hello-world-internationalized")
public String helloWorldInternationalized(#RequestHeader(name="Accept-Language", required=false) Locale locale) {
return messageSource.getMessage("good.morning.message", null, locale);
}
//other version of the method
#GetMapping(path="/hello-world-internationalized")
public String helloWorldInternationalizedVersion2() {
return messageSource.getMessage("good.morning.message", null, LocaleContextHolder.getLocale());
}
}
In Spring Boot #RestController class, is it not possible to overload a method and use the same url for both?
I'd appreciate any comment. Your comments will help me understand this scenario better.
Thank you.
Yes and no. What you currently have is 2 get methods mapped to the same URL, there is no differentiator in the mapping for Spring to determine which one to use when a request comes in.
As you are using a request header in your second one, you could use the headers element on the #GetMapping to add an additional mapping info.
#GetMapping(path="/hello-world-internationalized", headers="Accept-Language")
Now if that specific header is present the method will now be called.
It is important that the mapping information (or metadata) needs to be unique for each method, if the information is the same you will get an error.
You can't do that.
The integration of web framework and spring ioc in spring mvc is accomplished by monitoring the creation of servlet context through ContextLoaderListener and then loading the parent container. Then, by configuring a servlet object DispatcherServlet, the specific sub-container is loaded when DispatcherServlet is initialized.
So,overloading cannot be implemented with the same url。
You can implement overloading in Service.

Starter autoconfig bean always takes precedence over custom autoconfig bean

I'm trying to make a custom spring boot starter that will be used by multiple projects to authenticate with Azure AD. All the Azure AD config has been set up and an individual project hardcoded with all the settings to work with Azure AD works fine too. Now I'm trying to move these settings into a custom Spring Boot starter so that multiple projects can use it. It works for the most part, except for one thing: moving the bean config for a custom AADAppRoleStatelessAuthenticationFilter. If I leave my custom implementation (CustomAADAppRoleStatelessAuthFilter) hardcoded in the actual implementing project, everything works and only CustomAADAppRoleStatelessAuthFilter is created, but as soon as I move it into the custom starter, I only ever get AADAppRoleStatelessAuthenticationFilter instead.
Note that my CustomAADAppRoleStatelessAuthFilter extends the starter's
AADAppRoleStatelessAuthenticationFilter.
The autoconfig for AADAppRoleStatelessAuthenticationFilter in the azure-spring-boot project (https://github.com/microsoft/azure-spring-boot/blob/master/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java) is:
#Bean
#ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
#ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter azureADStatelessAuthFilter(ResourceRetriever resourceRetriever) {
//bean details omitted
}
My custom autoconfig that should replace the above is as follows:
#Bean
#ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
#ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
ResourceRetriever resourceRetriever) {
return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}
No amount of #AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) works.
Also, if I change the condition on my custom bean to be the subtype (#ConditionalOnMissingBean(CustomAADAppRoleStatelessAuthFilter.class)), BOTH types get created, and I can autowire my CustomAwareAADAppRoleStatelessAuthFilter and put it in my WebSecurityConfigurerAdapter, but things STILL won't work. I debugged things and found that the CustomAADAppRoleStatelessAuthFilter is the only bean of the ADAppRoleStatelessAuthenticationFilter type in my spring security filter chain, but that once the 'end of the additional filter chain' has completed and the 'original chain proceeds', I find that the ADAppRoleStatelessAuthenticationFilter has fired! And of course it throws an error because my CustomAADAppRoleStatelessAuthFilter has already done things to customize the UserPrincipal. I can't figure out where the ADAppRoleStatelessAuthenticationFilter is getting added to any filter chain, and even if I mark my CustomAADAppRoleStatelessAuthFilter bean with #Primary, the starter ADAppRoleStatelessAuthenticationFilter will still be used instead.
The only 'solutions' that have worked are to define the CustomAADAppRoleStatelessAuthFilter in the actual implementing project instead of the custom starter project, or to exclude the AADAuthenticationFilterAutoConfiguration in my actual implementing project's #SpringBootApplication annotation (Not even excluding it the property-based way works).
Is there a way to make AADAuthenticationFilterAutoConfigurations ADAppRoleStatelessAuthenticationFilter bean definition back off? 'Cause #AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) on my custom auto configuration class that has my CustomAADAppRoleStatelessAuthFilter definition doesn't work, and having all the implementing projects explicitly exclude AADAuthenticationFilterAutoConfiguration isn't the most ideal solution (although at least with that solution they don't all need to declare their own bean definition for CustomAADAppRoleStatelessAuthFilter).
Have you tried using #Order and assign your custom bean a higer precedence. By default all the beans gets lowest priority (Ordered.LOWEST_PRECEDENCE) losing to any other specified order value.
#Order(Ordered.LOWEST_PRECEDENCE - 1)
#Bean
#ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
#ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
ResourceRetriever resourceRetriever) {
return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}
Can you try putting the #Order(Ordered.LOWEST_PRECEDENCE - 1) like i mentioned above? Your bean should then take precedence over the other.
Have you tried adding #Primary to your bean?

Spring MVC Annotated Controller not found

I've got a really annoying issue... I've not defined a Controller handler mapper and therefore I'm using the DefaultAnnotationHandlerMapping class. My issue seems to have something to do with the Controller bean name mapping to the Controller class e.g.
Incoming request to dispatcher -> index.htm
With the following Controller class:
#Controller
public class IndexController {
#RequestMapping(value = "/index.htm", method = RequestMethod.GET)
public String loginForm(ModelMap model) {
return "index";
}
}
Should map index.htm to indexController bean and then to IndexController class. I can see from the logs that Spring has registered the bean with the container however I get the following error:
org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [xxx.controller.IndexController]
I know for certain that the class exists (because the bean name is derived from it).
The strange thing is that if I name my controller the same as the bean name (e.g. indexController) everything is fine!! (Obviously I don't want my classes to have a lower case first letter!!)
Just to note that I've used the following in my dispatcher-servlet.xml:
<mvc:annotation-driven />
<context:component-scan base-package="xxx" />
JLove
This error usually means your class has not been bundled in the war file. Unzip the war file and check your class is included correctly.
I suspect you successfully bundled "indexController.class" at some point, but prior packaging attempts failed. You haven't indicated how you are packaging your application, check your Maven/Ant configuration if you are using one of those. Beware of sharing build directories between IDE and build script, as they can occasionally conflict with each other.
I'd advice you to download STS and create new Spring MVC project template, to see, how Spring 3 MVC works.

Spring Security #Secured annotation and Scala controller

In a mixed Java/Scala Spring (v3.2) project I want to create a scala-based controller, and secure it with #Secure annotation. For java controllers this works just fine, but when I add #Secure annotation to the scala controller it disappears from URL mapping on application startup.
Sample code:
#Controller
#Secured(Array("ROLE_USER"))
class TestController {
#RequestMapping(value = Array("/show"), method = Array(RequestMethod.GET))
def show = {
"helloTemplate"
}
}
The same if I put secure annotation per method - the whole controller class will disappear from URL mapping (even if there are unsecured methods). No exceptions or warnings in the log. If I secure this URL via spring-security intercept-url in xml configuration - all things works fine (without #Secure annotation on controller). Spring and spring-security configuration via xml files and annotation-driven configuration is turned on. Thank you for any help.
In Spring 4/Spring you must enable proxyTargetClass = true to use CGLIB class proxying. This is superior to java class Proxying when working with scala.
SpringBoot
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)

Categories

Resources