I'm trying to set up a second resolver for SVGs which use thymeleaf attributes, so it needs to be resolved by the template engine within my spring boot application. My implementation so far:
ThymeleafSvgConfig
#Configuration
public class ThymeleafSvgConfig {
#Bean
public FileTemplateResolver svgTemplateResolver(){
FileTemplateResolver svgTemplateResolver = new FileTemplateResolver();
svgTemplateResolver.setPrefix("classpath:/svgtemplates/");
svgTemplateResolver.setSuffix(".svg");
svgTemplateResolver.setTemplateMode("XML");
svgTemplateResolver.setCharacterEncoding("UTF-8");
svgTemplateResolver.setOrder(0);
return svgTemplateResolver;
}
}
ThymeleafSvgController
#Controller
public class ThymeleafSvgController {
#Autowired
private ServletContext servletContext;
#Autowired
private TemplateEngine templateEngine;
//.. logging etc.
#RequestMapping(value = "/getSvg", method = { RequestMethod.GET, RequestMethod.POST })
public void getSvg(#RequestParam(required = false) String type, HttpServletRequest request, HttpServletResponse response) {
WebContext context = new WebContext(request, response, servletContext, request.getLocale());
templateEngine.process("test", context, response.getWriter());
}
}
I read that Spring Boot searches for resolvers automatically and adds them to the engine [s. this]. So I assumed, that my Config should be enough and that via autowiring I should get the correct template engine.
But still I receive the same error in my application, when trying to call:
templateEngine.process("test", context, response.getWriter());
My SVG is saved in /src/main/resources/svgtemplates/test.svg
Exception:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "test", template might not exist or might not be accessible by any of the configured Template Resolvers
I'm not sure what I misconfigured, so I need some help with this. Is the FileTemplateResolver the correct class for this? I also tried the ClassLoaderTemplateResolver but it threw the same exception. The standard Thymeleaf Resolver from spring boot works fine, which works with the standard prefix: "classpath:/templates/". So considering that 'templates' and 'svgtemplates' lie in the same folder, my prefix should be correct so far, shouldn't it?
Maybe someone else sees the flaw.
The problem was that the FileTemplateResolver can't cope with the 'classpath:' notation. After providing the complete file path, the resolver works.
You can use the SpringResourceTemplateResolver with Spring Boot which does support the classpath: notation:
#Bean
public ITemplateResolver svgTemplateResolver()
{
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/svg");
resolver.setSuffix(".svg");
resolver.setTemplateMode("XML");
return resolver;
}
You may also want to set spring.thymeleaf.template-resolver-order = 0 in you application configuration to prioritise the default resolver (typically used for the HTML templates).
Related
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";
}
I'm having problems with static content in Spring Boot when using a context path. i.e.: I wish to deploy my app to localhost:8080/{appname}/
When I run the app without context path everything works fine and Spring Boot finds and runs my .html files (from resources/templates/, I'm using Thymeleaf) and JS files (from resources/static/js/) but when I add the context path with either:
server.context-path=/{appname}
OR
server.servlet-path=/{appname}
then the .html pages are still displayed by the JS files generate 404 errors.
I have tried to change the spring.resources.static-locations in the application.properties and overriding addResourceHandlers() method in my MvcConfig.class but neither seem to work
I use a MvcConfig class because I need to define a CookieLocaleResolver and a MessageSource but that is all I have in the MvcConfig. I do not use #EnableWebMvc and just have the #SpringBootApplication annotation with a #ComponentScan.
Any help would be appreciated :)
As per your comment:
the HTML is referencing the JS without the app context
the problem is not in Spring serving the js, its that the page is not creating the URL to the resources correctly.
Thymeleaf provides a mechanism to support this automatically by just marking the src attribute with the th prefix.
See section 2 "Context-relative URLs": www.thymeleaf.org/doc/articles/standardurlsyntax.html
Below is an example shows how to configure static resources in spring boot.
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**/*.js").addResourceLocations("/ui/static/");
registry.addResourceHandler("/**/*.css").addResourceLocations("/ui/static/");
}
}
Path patterns
Add a resource handler for serving static resources based on the specified URL path patterns. The handler will be invoked for every incoming request that matches to one of the specified path patterns.
Patterns like "/static/" or "/css/{filename:\w+\.css}"} are allowed.
See **org.springframework.util.AntPathMatcher for more details on the syntax.
your jsp/html looks refer static content as in below
<link href="/webAppContext/cssa/bootstrap.min.css" rel="stylesheet"/>
<script src="/webAppContext/jquery-2.2.1.min.js"></script>
<script src="/webAppContext/bootstrap.min.js"></script>
urls using which browser tries to get static content
http://localhost:8080/webAppContext/jquery-2.2.1.min.js
http://localhost:8080/webAppContext/bootstrap.min.js
http://localhost:8080/webAppContext/cssa/bootstrap.min.css
server.servlet.context-path=/webAppContext is in your application.properties
My config is:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
public static final Logger LOG = LoggerFactory.getLogger(MvcConfig.class);
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setCookieMaxAge(-1);
cookieLocaleResolver.setDefaultLocale(Locale.FRENCH);
return cookieLocaleResolver;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:i18n/lang");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Urli18nRewriterInterceptor());
}
}
and folder structure:
folder structure
I have a Spring Boot web application that adds a resource handler for static resources (mainly CSS and JS files) using the addResourceHandlers method of WebMvcConfigurerAdapter. This resource handler is further configured to enable a VersionResourceResolver Basically this is
public class CustomWebMvcConfigurer extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
ResourceHandlerRegistration registration = registry.addResourceHandler(...)
ResourceChainRegistration chainRegistration = registration.resourceChain(...);
chainRegistration.addResolver(
new VersionResourceResolver().addContentVersionStrategy(...));
...
}
}
Now the problem is, when the user sees an error page (for example the 404 page), this VersionResourceResolver won't be used. (The error pages are configured using #ExceptionHandlers in a #ControllerAdvice annotated class.) The error pages will be rendered and displayed fine, however, the resources loaded on this page don't have version applied.
The Spring Boot documentation has a note saying that an ERROR dispatcher needs special treatment, however, I cannot figure out how to apply this advice in the context of our WebMvcConfigurerAdapter.
Any help is appreciated.
I had the same problem in a my opensource project and I resolve in this way.
I create an #Controller class like this:
#Controller
public class ExceptionController {
#Autowired
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping("/exception")
public String exception(Model model,Exception ex,Locale locale,HttpServletRequest httpRequest,HttpServletResponse httpResponse){
ex.printStackTrace();
model.addAttribute("templatePath", "exception/exception");
model.addAttribute("template", "content");
try{
model.addAttribute("exceptionMessage",messageSource.getMessage(String.format("exception.body.%s",httpResponse.getStatus()),new Object[]{},locale));
} catch (NoSuchMessageException e){
model.addAttribute("exceptionMessage",messageSource.getMessage("exception.body",new Object[]{},locale));
}
return "index";
}
}
and then I customize the servlet container configuration in this way:
#Bean
public EmbeddedServletContainerCustomizer exceptionHandling() {
return container -> container.addErrorPages(new ErrorPage("/exception"));
}
in feew words I make a special page for error /exception in this case and then configure the embedded tomcat for use this url in case of exceptions
In the Autoconfiguration classes of Spring boot you can see in org.springframework.boot.autoconfigure.web.BasicErrorController this documentation:
Basic global error {#link Controller}, rendering {#link ErrorAttributes}. More specific
* errors can be handled either using Spring MVC abstractions (e.g.
* {#code #ExceptionHandler}) or by adding servlet
* {#link AbstractEmbeddedServletContainerFactory#setErrorPages container error pages}
I hope that tis can help you
The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.
Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component
I'm trying to make "hello world" application with gradle, spring boot and spring mvc with the simplest view resolver and html.
I started from the thymeleaf spring boot example and I just wanted to remove thymeleaf to make a simpler mvc application using pure html and InternalResourceViewResolver. I have a single greeting.html I want to serve which is located at src/main/webapp/WEB-INF. When I run the app I get
No mapping found for HTTP request with URI [/WEB-INF/greeting.html] in DispatcherServlet with name 'dispatcherServlet'
This is a common error and there are a lot of answers on the web but nothing seems to help.
Here is my Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here is my GreetingController.java
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
}
Here is my MvcConfiguration.java
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
}
I run it with gradle bootRun
Here is the repo with the code: https://github.com/driver-pete/spring-mvc-example
Here are some more clues:
Thymeleaf view resolving works fine
InternalResourceViewResolver resolves to the right path
WEB-INF and greeting.html seems to be present in the war file
I do not have jsp or jstl so I do not miss those jars as some might suggest
My hypothesis is that dispatcher servlet somehow get configured to serve on /* instead of / like here and everywhere. However I don't have web.xml so those advices do not apply here. I see a lot of examples how to configure dispatcher servlet programmatically but I want to keep my app at minimum and I suspect that spring boot is supposed to configure it ok since it works fine with thymeleaf.
You only need to enable the default servlet, this is done by adding the following to your MvcConfiguration:
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Essentially what is happening is Spring does not know how to handle the handling of such content natively(could be a jsp say), and to this configuration is the way to tell it to delegate it to the container.
View resolver can also be configured in application.properties file of Spring-Boot web applications, something like below:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
After investigating more I discovered an alternative solution that works without adding configureDefaultServletHandling method. You need to add an embedded tomcat jsp engine to build.gradle:
compile("org.apache.tomcat.embed:tomcat-embed-jasper")
As opposed to configureDefaultServletHandling method this solution works not only with plain html but also with jsp.
All solutions are available at: https://github.com/driver-pete/spring-mvc-example
This solution is available on master.
Biju's solution is on DefaultServletHandling_solution branch.
If you are using spring above 5.0, then use org.springframework.web.servlet.view.InternalResourceViewResolver
instead of
org.springframework.web.servlet.InternalResourceViewResolver
in your bean definition