Spring Boot MVC Remote Templates - spring

I want to put our templates folder in a remote host like aws-s3, currently
using a custom urlTemplateResolver and using the thymeleaf replace tag.
#Configuration
public class SpringTemplateConfiguration {
private final SpringResourceTemplateResolver springResourceTemplateResolver;
#Value("${spring.thymeleaf.cache}")
Boolean springThymeleafCache;
public SpringTemplateConfiguration(SpringResourceTemplateResolver springResourceTemplateResolver) {
this.springResourceTemplateResolver = springResourceTemplateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
UrlTemplateResolver urlTemplateResolver = new UrlTemplateResolver();
urlTemplateResolver.setCacheable(springThymeleafCache); // explicit set cacheable, otherwise it will be always cached
templateEngine.addTemplateResolver(urlTemplateResolver);
templateEngine.addTemplateResolver(springResourceTemplateResolver);
return templateEngine;
}
}
and this is the thymeleaf template
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{${url} :: html}">
</html>
is there another way to just use the templates folder from the remote host and just return the view name from the controller ??
It is possible to use another external location but not remote location which doesn't solve our problem.

Related

Could not resolve view with name 'index' in Spring Boot

Spring boot: 2.3.3.RELEASE
Java: 11
I use webflux + RouterFunction + Thymeleaf and encounter the error "Could not resolve view with name 'index'".
index.html is under "resources/templates".
I put some source code looks important.
Are we not able to use Thymeleaf if we use "RouterFunction"?
Please feel free to put a comment if you need more detail.
######## handler ###########
#Component
public class ItemHandler {
public RouterFunction<ServerResponse> routes = route()
.path("/item", builder -> builder.GET("/", this::index))
.build();
public Mono<ServerResponse> index(ServerRequest request) {
Map<String, Object> attributes = new HashMap<>();
attributes.put("items", "Hello");
return ServerResponse.ok().contentType(MediaType.TEXT_HTML)
.render("index", attributes);
}
}
######## index.html ###########
<!DOCTYPE html>
<html lang="ja"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<h1>FluxTest</h1>
</body>
</html>
######## entry point ###########
#SpringBootApplication
public class DemoWebfluxApplication {
public static void main(String[] args) {
SpringApplication.run(DemoWebfluxApplication.class, args);
}
}
The default property in charge of handling the location of static files is spring.resources.static-locations.
The default values are /META-INF/resources/, /resources/, /static/, /public/. You can either override the default values or put your index.html in one of these locations.

How to change swagger-ui.html default path

I wanna change my swagger-ui path from localhost:8080/swagger-ui.html to
localhost:8080/myapi/swagger-ui.html in springboot
redirect is helpless to me
In the application.properties of Spring Boot
springdoc.swagger-ui.path=/swagger-ui-custom.html
in your case it will be
springdoc.swagger-ui.path=/myapi/swagger-ui.html
if for some reason you don't want redirect to /swagger-ui.html you can set itself contents as home view, setting an index.html at resources/static/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to another awesome Microservice</title>
</head>
<body>
<script>
document.body.innerHTML = '<object type="text/html" data="/swagger-ui.html" style="overflow:hidden;overflow-x:hidden;overflow-y:hidden;height:100%;width:100%;position:absolute;top:0px;left:0px;right:0px;bottom:0px"></object>';
</script>
</body>
</html>
then accessing to your http://localhost:8080/ you will see your swagger docs.
finally you can customize path and .html file using:
registry.addViewController("/swagger").setViewName("forward:/index.html");
like suggests this answer
You can modify springfox properties in application.properties
For example, to edit the base-url
springfox.documentation.swagger-ui.base-url=documentation
For e.g. setting it to /documentation will put swagger-ui at /documentation/swagger-ui/index.html
If you want to add, for example, documentation prefix - You can do like this for path http://localhost:8080/documentation/swagger-ui.html:
kotlin
#Configuration
#EnableSwagger2
#ConfigurationPropertiesScan("your.package.config")
#Import(value = [BeanValidatorPluginsConfiguration::class])
class SwaggerConfiguration(
private val swaggerContactProp: SwaggerContactProp, private val swaggerProp: SwaggerProp
) : WebMvcConfigurationSupport() {
// https://springfox.github.io/springfox/docs/current/
#Bean
fun api(): Docket = Docket(DocumentationType.SWAGGER_2)
.groupName("Cards")
.apiInfo(getApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("your.controllers.folder"))
.paths(PathSelectors.any())
.build()
private fun getApiInfo(): ApiInfo {
val contact = Contact(swaggerContactProp.name, swaggerContactProp.url, swaggerContactProp.mail)
return ApiInfoBuilder()
.title(swaggerProp.title)
.description(swaggerProp.description)
.version(swaggerProp.version)
.contact(contact)
.build()
}
override fun addViewControllers(registry: ViewControllerRegistry) {
with(registry) {
addRedirectViewController("/documentation/v2/api-docs", "/v2/api-docs").setKeepQueryParams(true)
addRedirectViewController(
"/documentation/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui"
)
addRedirectViewController(
"/documentation/swagger-resources/configuration/security", "/swagger-resources/configuration/security"
)
addRedirectViewController("/documentation/swagger-resources", "/swagger-resources")
}
}
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/documentation/**").addResourceLocations("classpath:/META-INF/resources/")
}
}
#ConfigurationProperties(prefix = "swagger")
#ConstructorBinding
data class SwaggerProp(val title: String, val description: String, val version: String)
#ConfigurationProperties(prefix = "swagger.contact")
#ConstructorBinding
data class SwaggerContactProp(val mail: String, val url: String, val name: String)
and in applicatiom.yml:
swagger:
title: Cards
version: 1.0
description: Documentation for API
contact:
mail: email#gmail.com
url: some-url.com
name: COLABA card
Also don't forget to add in build.gradle.kts:
implementation("io.springfox:springfox-swagger2:$swagger")
implementation("io.springfox:springfox-swagger-ui:$swagger")
implementation("io.springfox:springfox-bean-validators:$swagger")
I've found several possible solutions for myself. Maybe it will be helpful for somebody else.
Set springdoc.swagger-ui.path directly
The straightforward way is to set property springdoc.swagger-ui.path=/custom/path. It will work perfectly if you can hardcode swagger path in your application.
Override springdoc.swagger-ui.path property
You can change default swagger-ui path programmatically using ApplicationListener<ApplicationPreparedEvent>. The idea is simple - override springdoc.swagger-ui.path=/custom/path before your Spring Boot application starts.
#Component
public class SwaggerConfiguration implements ApplicationListener<ApplicationPreparedEvent> {
#Override
public void onApplicationEvent(final ApplicationPreparedEvent event) {
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
Properties props = new Properties();
props.put("springdoc.swagger-ui.path", swaggerPath());
environment.getPropertySources()
.addFirst(new PropertiesPropertySource("programmatically", props));
}
private String swaggerPath() {
return "/swagger/path"; //todo: implement your logic here.
}
}
In this case, you must register the listener before your application start:
#SpringBootApplication
#OpenAPIDefinition(info = #Info(title = "APIs", version = "0.0.1", description = "APIs v0.0.1"))
public class App {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(App.class);
application.addListeners(new SwaggerConfiguration());
application.run(args);
}
}
Redirect using controller
You can also register your own controller and make a simple redirect as suggested there.
Redirect code for Spring WebFlux applications:
#RestController
public class SwaggerEndpoint {
#GetMapping("/custom/path")
public Mono<Void> api(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
response.getHeaders().setLocation(URI.create("/swagger-ui.html"));
return response.setComplete();
}
}
The problem with such an approach - your server will still respond if you call it by address "/swagger-ui.html".
You can use this code, it worked for me
package com.swagger.api.redirect;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class SwaggerApiReDirector implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/documentation/v2/api-docs", "/v2/api-docs");
registry.addRedirectViewController("/documentation/configuration/ui", "/configuration/ui");
registry.addRedirectViewController("/documentation/configuration/security", "/configuration/security");
registry.addRedirectViewController("/documentation/swagger-resources", "/swagger-resources");
registry.addRedirectViewController("/documentation/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
registry.addRedirectViewController("/documentation", "/documentation/swagger-ui.html");
registry.addRedirectViewController("/documentation/", "/documentation/swagger-ui.html");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/documentation/**").addResourceLocations("classpath:/META-INF/resources/");
}
}
If you are using spring boot then please update
application.properties file and write here
server.servlet.context-path=/myapi
it will redirect you as you want.

Spring Boot, static resources and mime type configuration

I'm facing a Spring Boot configuration issue I can't deal with...
I'm trying to build an HelloWorld example for HbbTV with Spring Boot, so I need to serve my "index.html" page with mime-type="application/vnd.hbbtv.xhtml+xml"
my index.html will be accessed as a static page, for instance http://myserver.com/index.html?param=value.
with the following code, no matter how hard I try, I get a text/html content type.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//HbbTV//1.1.1//EN" "http://www.hbbtv.org/dtd/HbbTV-1.1.1.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>MyApp HBBTV</title>
<meta http-equiv="content-type" content="Content-Type: application/vnd.hbbtv.xhtml+xml; charset=UTF-8" />
</head>
<body>
...
</body>
</html>
So I tried to add a "home()" endpoint into a #Controller to force the correct mime-type, and that works.
#RestController
public class HbbTVController {
#RequestMapping(value = "/hbbtv", produces = "application/vnd.hbbtv.xhtml+xml")
String home() {
return "someText";
}
...
}
"That works" mean the jetty server serves me a html file with the correct content-type containing the test someText.
My next try were to replace the #RestController by #Controller (same produce config), and replace "someText" by index.html
#Controller
public class HbbTVController {
#RequestMapping(value = "/hbbtv", produces = "application/vnd.hbbtv.xhtml+xml")
String home() {
return "index.html";
}
...
}
Well, it serves my index.html correctly, but the Content-Type is wrong : text/html instead of application/vnd.hbbtv.xhtml+xml.
Furthermore, I don't want to access to myserver.com/hbbtv to get index.html, but directly to myserver.com/index.html.
How could I do that ?
Thanks...
Well, finally, I found the "Spring boot compliant solution". It's the same as Jamie Birch suggested, but realized with Spring mechanisms.
Spring Boot 1:
#Configuration
public class HbbtvMimeMapping implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
mappings.add("html", "application/vnd.hbbtv.xhtml+xml; charset=utf-8");
mappings.add("xhtml", "application/vnd.hbbtv.xhtml+xml; charset=utf-8");
container.setMimeMappings(mappings);
}
}
Spring Boot 2:
#Configuration
public class HbbtvMimeMapping implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
#Override
public void customize(ConfigurableServletWebServerFactory factory) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
mappings.add("html", "application/vnd.hbbtv.xhtml+xml; charset=utf-8");
mappings.add("xhtml", "application/vnd.hbbtv.xhtml+xml; charset=utf-8");
factory.setMimeMappings(mappings);
}
}
I'll extend comment providen by #Cheloute
Sping boot have default mime types
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/MimeMappings.java
to override already setted mime type you should remove it first
Here is example what I used to override js and css
#Configuration
public class CustomServletConfiguration implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
#Override
public void customize(ConfigurableServletWebServerFactory factory) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
mappings.remove("js");
mappings.add("js", "application/javascript;charset=utf-8");
mappings.remove("css");
mappings.add("css", "text/css;charset=utf-8");
factory.setMimeMappings(mappings);
factory.setPort(9000);
}
}
Can't help with the Spring Boot side, but if you get no other responses, try these:
Set the file-type as .xhtml rather than .html.
Provide a mapping from .xhtml to MIME type application/vnd.hbbtv.xhtml+xml on your Jetty server's mime.properties file. A few more details on how to do that here.

spring boot calling thymeleaf template failed

im getting this error i searched on the same issues like mine on stack and i have foudn that i shouldn t put .html when calling it but im getting the same error :
`Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving template "orderConfirmationEmailTemplate", template might not exist or might not be accessible by any of the configured Template Resolver`s
my mail constructor :
#Component
public class MailConstructor {
#Autowired
private Environment env;
#Autowired
private TemplateEngine templateEngine;
public SimpleMailMessage constructNewUserEmail(User user, String password) {
String message="\nPlease use the following credentials to log in and edit your personal information including your own password."
+ "\nUsername:"+user.getUsername()+"\nPassword:"+password;
SimpleMailMessage email = new SimpleMailMessage();
email.setTo(user.getEmail());
email.setSubject("Le's Bookstore - New User");
email.setText(message);
email.setFrom(env.getProperty("support.email"));
return email;
}
public MimeMessagePreparator constructOrderConfirmationEmail (User user, Order order, Locale locale) {
Context context = new Context();
context.setVariable("order", order);
context.setVariable("user", user);
context.setVariable("cartItemList", order.getCartItemList());
String text = templateEngine.process("orderConfirmationEmailTemplate.html", context);
MimeMessagePreparator messagePreparator = new MimeMessagePreparator() {
#Override
public void prepare(MimeMessage mimeMessage) throws Exception {
MimeMessageHelper email = new MimeMessageHelper(mimeMessage);
email.setTo(user.getEmail());
email.setSubject("Order Confirmation - "+order.getId());
email.setText(text,true);
email.setFrom(new InternetAddress("alaaeddinezammel1993#gmail.com"));
}
};
return messagePreparator;
}
and im calling it from rest service:
mailSender.send(mailConstructor.constructOrderConfirmationEmail(user, order, Locale.ENGLISH));
shoppingCartService.clearShoppingCart(shoppingCart);
and im putting the file .html under package in the project
In your question, the TemplateEngine is auto wired so I cannot see how it is configured but it in order to discover your template from the location com.bookstore.domain.security.templates the configuration should look something like this:
#Bean
public TemplateEngine templateEngine() {
final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(templateResolver());
return templateEngine;
}
private ITemplateResolver templateResolver() {
final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix(“/com/bookstore/domain/security/templates“);
templateResolver.setSuffix(".html");
…
return templateResolver;
}
In this code I am configuring the TemplateEngine in code, perhaps you are using XML. Regardless of how you are configuring the TemplateEngine, you are clearly using Spring to do so (since you inject it into your MailConstructor), the key point here is that regardless of how you configure it you need to tell it where to find your template and the way to do that is to invoke the ITemplateResolver's setPrefix() method.
Plenty more details in the article titled Sending email in Spring with Thymeleaf in the Thymeleaf docs.
actually with my configuration putted in my question i just put the .html file under file called templates under resources and it works the mail is sent , spring boot is apparently auto configured with this path without configuring the
templateResolver

Thymeleaf : template might not exist or might not be accessible by any of the configured Template Resolvers

I have this code:
private static final String EMAIL_INLINEIMAGE_TEMPLATE_NAME = "templateemail.html";
#Bean
public TemplateEngine emailTemplateEngine() {
templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(this.htmlTemplateResolver());
)
templateEngine.setTemplateEngineMessageSource(this.messageSource);
return templateEngine;
}
private static ITemplateResolver htmlTemplateResolver() {
final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(Integer.valueOf(0));
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateResolver.DEFAULT_TEMPLATE_MODE);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCacheable(false);
return templateResolver;
}
public void sendEmail(String emailAddress, String title, String body, Locale local, String image) {
if (Boolean.parseBoolean(isEmailServiceActivated)) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper mailMsg = new MimeMessageHelper(mimeMessage);
try {
mailMsg.setFrom(EMAIL_USERNAME);
mailMsg.setTo(emailAddress);
mailMsg.setSubject(title);
// Prepare the evaluation context
ctx.setLocale(local);
ctx.setVariable("imageHeaderResourceName", HEADER_LOGO_IMAGE);
ctx.setVariable("body", body);
ctx.setVariable("imageResourceName", image);
final String htmlContent = this.templateEngine.process(new ClassPathResource(EMAIL_INLINEIMAGE_TEMPLATE_NAME).getPath(), ctx);
mailMsg.setText(htmlContent, true );
mailMsg.addInline(HEADER_LOGO_IMAGE, new ClassPathResource(HEADER_LOGO_IMAGE ) , PNG_MIME);
mailMsg.addInline(image, new ClassPathResource(image) , PNG_MIME);
} catch (MessagingException e) {
e.printStackTrace();
}
mailSender.send(mimeMessage);
}
}
I have templateemail.html file under /templates/ directory. when I launch the sending email method I have this exception :
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "templateemail.html", template might not exist or might not be accessible by any of the configured Template Resolvers
I dont know if it's because the templateEngine can't find my file (I try even with tomcat absolute path and /bin directory but no way ) or I haven't configure the right Template Resolver.
Thank you very much for your help. I
It work now by deleting ".html" in the name of template (the file has the html extension)
private static final String EMAIL_INLINEIMAGE_TEMPLATE_NAME = "templateemail"
Note the non-cross-platform behavior that can occure: on Windows the CamelCase.html template was resolved, but not on Ubuntu linux! There I had to rename it to camelcase.html, all chars in lower case
I had the same issue recently. My problem was that my template had references to other templates that started with /.
For example:
<html ... th:include="/internal/layout-normal :: page"> <-- failed
<html ... th:include="internal/layout-normal :: page"> <-- worked
Both variants worked without problems when I run the application from IntelliJ. However, when packaged and run via java -jar the first line failed.
Removing the / solved the problem for me
Thymeleaf picks up the html files from resources/templates path by using the name of html file.
Hence in this case just removing .html extension from the filename would work!

Resources