Spring Boot displays images in some pages but not others - spring-boot

I'm facing issues with images in my Spring Boot + Thymeleaf application.
After reading a lot of fixes I'm able to show images in some of my app's pages but in other pages the images doesn't show.
I think that the number of paths in the request made is involved. It seems that requests to /myaction render pages showing images while requests to /myaction/other render pages not showing images.
In the fomer the successful request to obtain the images is:
http://localhost:8080/myapp/images/logo.png
In the latter the failed request to obtain the images is:
http://localhost:8080/myapp/myaction/images/logo.png
I append my configuration:
In my implementation of WebMvcConfigurerAdapter:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"
};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/**")
.addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
Controller class:
#Controller
#RequestMapping(path="/myaction")
public class PagosController {
#GetMapping(path="")
public String show(Model model) {
//...
}
#GetMapping(path="/other")
public String show2(Model model) {
//...
}
}
In my html template I load the image this way:
<img th:src="#{images/logo.png}" />
logo.png
The file logo.png is located in src/main/resources/static/images
I have no clue why this is happening. Any idea on why images are getting requested at http://localhost:8080/myapp/myaction/images/logo.png? Thanks in advance.

According to your configuration, the images are available from the root /.
So you should be able to use <img th:src="#{/images/logo.png}" /> in any page.

Related

How to redirect springboot error page to vaadin error UI?

I have an error page in vaadin UI. But sometimes we can write a wrong url and we see the springboot error page.
I want to show vaadin UI error page in this case. By the way, I have already have a rendered 404 page for springboot. But I don't want to show it.
This is my vaadin error UI. But this works into the application. (http://localhost:7001/MyApplication/#!error)
sometimes I write an invalid url like this: http://localhost:7001/MyApplication/blablablabla
in this case I want to redirect vaadin error page (http://localhost:7001/MyApplication/#!error) But the springboot redirects me to rendered 404 page.
It is possible?
#UIScope
#SpringView(name = ErrorView.VIEW_NAME)
public class ErrorView extends VerticalLayout implements View {
public static final String VIEW_NAME = "error";
private Label explanation;
public ErrorView() {
Label header = new Label("The view could not be found");
header.addStyleName(MaterialTheme.LABEL_H1);
addComponent(header);
addComponent(explanation = new Label());
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
explanation.setValue(String.format("You tried to navigate to a view ('%s') that does not exist.", event.getViewName()));
getUI().getNavigator().navigateTo(ErrorView.VIEW_NAME);
}
}
you can put 404/500 page under resources/error/ folder, Spring boot will redirect those page automatic when have error
I think SpringNavigator can solve your problem. While defining your Navigator, you can also define error View. See the example below
#SpringUI(path = "ui")
public class DemoUI extends com.vaadin.ui.UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
SpringNavigator navigator = new SpringNavigator();
navigator.setErrorView(new ErrorView());
setNavigator(navigator);
}
}

404 on Properly mapped SpringBoot RestController

I'm experiencing a little issue that is wasting a lot of my time...
I've created, for demonstration purposes, a simple SpringBoot application using the Eclipse New > Spring Starter Project.
Here is my Application class:
package it.asirchia;
//All needed imports
#SpringBootApplication
public class Application {
public static HashMap<Long,Book> books = new HashMap<Long, Book>();
public static HashMap<Long,Editor> editors = new HashMap<Long, Editor>();
public static HashMap<Long,Person> authors = new HashMap<Long, Person>();
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Then I've created the EditorsApis Controller:
package it.asirchia.apis;
//All needed imports
#RestController
#RequestMapping(value="/editors")
public class EditorsApis {
private static long counter = 0;
#RequestMapping(value="/", method=RequestMethod.GET)
public HashMap<Long, Editor> getAllEditor(){
return Application.editors;
}
#RequestMapping(value="/", method=RequestMethod.POST)
public void postNewEditor(#RequestBody Editor editor){
Application.editors.put(counter++, editor);
}
#RequestMapping(value="/{editorid}", method=RequestMethod.PUT)
public void updateEditor(#PathVariable long editorid,
#RequestBody Editor editor){
Application.editors.put(editorid, editor);
}
#RequestMapping(value="/{editorid}", method=RequestMethod.GET)
public Editor getEditor(#PathVariable long editorid){
return Application.editors.get(editorid);
}
#RequestMapping(value="/{editorid}", method=RequestMethod.DELETE)
public void deleteEditor(#PathVariable long editorid){
Application.editors.remove(editorid);
}
}
And an AuthorsApis and a BooksApis controllers that are very similar to the EditorApis one.
Of course I've created too all the three Pojos:
Editor.class, Person.class and Book.class
I've started up the Eclipse embedded Spring runtime and I can see that all the paths are properly mapped:
INFO [main] s.w.s.m.m.a.RequestMappingHandlerMapping Mapped "
{[/authors/],methods=[GET]}" onto public java.util.HashMap it.asirchia.apis.AuthorsApis.getAllAuthors()
And so on and so forth for all the other Rest APIs I've implemented.
The last three lines of the log are:
Starting beans in phase 0
Tomcat started on port(s): 8080 (http)
Started Application in 5.547 seconds (JVM running for 6.169)
Ok, for me wverything is properly configured, up and running. But when I try to invoke
GET /authors HTTP/1.1
Host: localhost:8080
I obtain:
{
"timestamp": 1507286437765,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/authors"
}
And the same happens for ALL the REST APIs I've implemented.
Any idea about the reason of this problem?
Thank you.
The following mapping will work localhost:8080/authors/ for you.Since in your method mapping GET you have added the "/" so you should provide the trailing slash in URL also. If you want mapping like this localhost:8080/authors then follow the below code,
#RequestMapping(value={"","/"}, method=RequestMethod.GET)
public HashMap<Long, Editor> getAllEditor(){
return Application.editors;
}
The above will accept,
1) localhost:8080/editors
2) localhost:8080/editors/
Hope this will help.
Can you just try to add a action content in value.Here only specifying only a "/".Like
#RequestMapping(value="/updateEditor", method=RequestMethod.GET)
If you need to add any path variable,you can modify the same with following,
#RequestMapping(value="/updateEditor/{editorid}", method=RequestMethod.PUT)
Just try this method also.

configuring Spring MVC welcome page

I was trying for two days to set up the index page using spring 4.
After many attempts I got a piece of code that worked
But I did not understand why it worked.
Could you tell me why.
public class MyWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer
{
protected Class<?>[] getRootConfigClasses() {
return new Class[] {AplicationConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[] {WebConfiguration .class};
}
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
Here's the snippet of code I found
// #Bean
// public InternalResourceViewResolver internalResourceViewResolver() {
// InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
// viewResolver.setPrefix("/WEB-INF/html/");
// viewResolver.setSuffix(".html");
// return viewResolver;
// }
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/*.html").addResourceLocations("/WEB-INF/html/");
super.addResourceHandlers(registry);
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController( "/" ).setViewName( "forward:/index.html" );
// registry.setOrder( Ordered.HIGHEST_PRECEDENCE );
// super.addViewControllers( registry );
}
my folders
1) why InternalResourceViewResolver do not work?
2) How does addResourceHandler and addViewController work? What are these paths?
3)And how do I add the css and javascripts file to the index page? like
<link href="/css/bootstrap.min.css" rel="stylesheet">
The page loads but this without the css and javascript files
I want to thank everyone who helped me because I'm a newbie in spring, thanks guys.
Only configuring InternalResourceViewResolver is not enough to serve static html. You also need to bind a path through controller to serve the page. As this is just a static page you can do that with
addViewControllers.
registry.addViewController("/").setViewName("index");
2.
addResourceHandlers: Add handlers to serve static resources such as images, js, and, css files from specific locations under web application root, the classpath, and others.
addViewControllers: Configure simple automated controllers pre-configured with the response status code and/or a view to render the response body. This is useful in cases where there is no need for custom controller logic -- e.g. render a home page, perform simple site URL redirects, return a 404 status with HTML content, a 204 with no content, and more.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html#addResourceHandlers-org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry-
3.
To make the resources(js, css, img) available, you need to add following configuration inside your addResourceHandlers methods.
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
And then to include css or js in a JSP page, you can use JSTL tag c:url or Spring tag spring:url.
<link href="<c:url value="/css/styles.css" />" rel="stylesheet">
<spring:url value="/css/styles.css" var="mainCss" />
<link href="${mainCss}" rel="stylesheet" />

Serve static html files omitting the .html extension in the url

With the fine way to serve static html/css/js resources from src/main/resources/static, is there a way to have some form of url abstraction?
Precisely, I would like to remove the .html ending from the urls.
To return html static file without extension is the same as return view name from any of templates engines (jsp, theamleaf, freemarker), the thing here you do not need to run any templates processing on view file, you just return it as it is.
Add code below to your Spring configuration:
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix("pages/");
internalResourceViewResolver.setSuffix(".html");
return internalResourceViewResolver;
}
Be careful /pages folder should be inside your ResourceLocation
, that means ResourceLocation + "/pages/" + pageName.html in browser should give you desired file (if you already configured servicing of static files it will not be a problem for you to find your ResourceLocation, check method addResourceHandlers in your WebMvcConfigurer)
Now at your WebMvcConfigurer you can add:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/admin").setViewName("admin");
registry.addViewController("/contact").setViewName("contact");
registry.addViewController("/error").setViewName("index");
}
or just use it, as you would use it in usual view resolver within MVC controller
#GetMapping("/")
public String greeting() {
return "index";
}
A request mapping that ignores all URLs that have a file extension and adds .html to URLs with no file extension.
#Controller
...
#RequestMapping("/{page:^(?!.*[.].*$).*$}")
public String requestOther(#PathVariable("page") String page) {
return "/"+page+".html";
}
...
Change the "/" to a sub directory if needed e.g. "/pages/".

Load images from outside of webapps / webcontext / deploy folder using <h:graphicImage> or <img> tag

I need to display images which reside outside of deploy folder in web application using JSF <h:graphicimage> tag or HTML <img> tag. How can I achieve that?
To the point, it has to be accessible by a public URL. Thus, the <img src> must ultimately refer a http:// URI, not something like a file:// URI or so. Ultimately, the HTML source is executed at enduser's machine and images are downloaded individually by the webbrowser during parsing the HTML source. When the webbrowser encounters a file:// URI such as C:\path\to\image.png, then it will look in enduser's own local disk file system for the image instead of the webserver's one. This is obviously not going to work if the webbrowser runs at a physically different machine than the webserver.
There are several ways to achieve this:
If you have full control over the images folder, then just drop the folder with all images, e.g. /images directly in servletcontainer's deploy folder, such as the /webapps folder in case of Tomcat and /domains/domain1/applications folder in case of GlassFish. No further configuration is necessary.
Or, add a new webapp context to the server which points to the absolute disk file system location of the folder with those images. How to do that depends on the container used. The below examples assume that images are located in /path/to/images and that you'd like to access them via http://.../images.
In case of Tomcat, add the following new entry to Tomcat's /conf/server.xml inside <Host>:
<Context docBase="/path/to/images" path="/images" />
In case of GlassFish, add the following entry to /WEB-INF/glassfish-web.xml:
<property name="alternatedocroot_1" value="from=/images/* dir=/path/to" />
In case of WildFly, add the following entry inside <host name="default-host"> of /standalone/configuration/standalone.xml ...
<location name="/images" handler="images-content" />
... and further down in <handlers> entry of the very same <subsystem> as above <location>:
<file name="images-content" path="/path/to/images" />
Or, create a Servlet which streams the image from disk to response:
#WebServlet("/images/*")
public class ImageServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String filename = request.getPathInfo().substring(1);
File file = new File("/path/to/images", filename);
response.setHeader("Content-Type", getServletContext().getMimeType(filename));
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
Files.copy(file.toPath(), response.getOutputStream());
}
}
If you happen to use OmniFaces, then the FileServlet may be useful as it also takes into account head, caching and range requests.
Or, use OmniFaces <o:graphicImage> which supports a bean property returning byte[] or InputStream:
#Named
#ApplicationScoped
public class Bean {
public InputStream getImage(String filename) {
return new FileInputStream(new File("/path/to/images", filename));
}
}
Or, use PrimeFaces <p:graphicImage> which supports a bean method returning PrimeFaces-specific StreamedContent.
#Named
#ApplicationScoped
public class Bean {
public StreamedContent getImage() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
}
else {
// So, browser is requesting the image. Return a real StreamedContent with the image bytes.
String filename = context.getExternalContext().getRequestParameterMap().get("filename");
return new DefaultStreamedContent(new FileInputStream(new File("/path/to/images", filename)));
}
}
}
For the first way and the Tomcat and WildFly approaches in second way, the images will be available by http://example.com/images/filename.ext and thus referencable in plain HTML as follows
<img src="/images/filename.ext" />
For the GlassFish approach in second way and the third way, the images will be available by http://example.com/context/images/filename.ext and thus referencable in plain HTML as follows
<img src="#{request.contextPath}/images/filename.ext" />
or in JSF as follows (context path is automatically prepended)
<h:graphicImage value="/images/filename.ext" />
For the OmniFaces approach in fourth way, reference it as follows
<o:graphicImage value="#{bean.getImage('filename.ext')}" />
For the PrimeFaces approach in fifth way, reference it as follows:
<p:graphicImage value="#{bean.image}">
<f:param name="filename" value="filename.ext" />
</p:graphicImage>
Note that the example #{bean} is #ApplicationScoped as it basically represents a stateless service. You can also make it #RequestScoped, but then the bean would be recreated on every single request, for nothing. You cannot make it #ViewScoped, because at the moment the browser needs to download the image, the server doesn't create a JSF page. You can make it #SessionScoped, but then it's saved in memory, for nothing.
See also:
Recommended way to save uploaded files in a servlet application
Simplest way to serve static data from outside the application server in a Java web application
Abstract template for a static resource servlet (supporting HTTP caching)
Show image as byte[] from database as graphic image in JSF page
Display dynamic image from database with p:graphicImage and StreamedContent
How to choose the right bean scope?
In order to achieve what you need using <h:graphicImage> or <img> tags, you require to create a Tomcat v7 alias in order to map the external path to your web app's context.
To do so, you will need to specify your web app's context. The easiest would be to define a META-INF/context.xml file with the following content:
<Context path="/myapp" aliases="/images=/path/to/external/images">
</Context>
Then after restarting your Tomcat server, you can access your images files using <h:graphicImage> or <img> tags as following:
<h:graphicImage value="/images/my-image.png">
or
<img src="/myapp/images/my-image.png">
*Note the context path is necessary for the tag but not for the
Another possible approach if you don't require the images to be available through HTTP GET method, could be to use Primefaces <p:fileDownload> tag (using commandLink or commandButton tags - HTTP POST method).
In your Facelet:
<h:form>
<h:commandLink id="downloadLink" value="Download">
<p:fileDownload value="#{fileDownloader.getStream(file.path)}" />
</h:commandLink>
</h:form
In your bean:
#ManagedBean
#ApplicationScope
public class FileDownloader {
public StreamedContent getStream(String absPath) throws Exception {
FileInputStream fis = new FileInputStream(absPath);
BufferedInputStream bis = new BufferedInputStream(fis);
StreamedContent content = new DefaultStreamedContent(bis);
return content;
}
}
}
In PrimeFaces you can implement your bean in this way:
private StreamedContent image;
public void setImage(StreamedContent image) {
this.image = image;
}
public StreamedContent getImage() throws Exception {
return image;
}
public void prepImage() throws Exception {
File file = new File("/path/to/your/image.png");
InputStream input = new FileInputStream(file);
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
setImage(new DefaultStreamedContent(input,externalContext.getMimeType(file.getName()), file.getName()));
}
In your HTML Facelet:
<body onload="#{yourBean.prepImage()}"></body>
<p:graphicImage value="#{youyBean.image}" style="width:100%;height:100%" cache="false" >
</p:graphicImage>
I suggest to set the attribute cache="false" in the graphicImage component.
In JSP
<img src="data:image/jpeg;base64,
<%= new String(Base64.encode(Files.readAllBytes(Paths.get("C:\\temp\\A.jpg"))))%>"/>
Packages are com.sun.jersey.core.util.Base64, java.nio.file.Paths and java.nio.file.Files.

Resources