Below, I am using login.html as the page where I am adding the image test.png within /static/images/
So, in login.html, I have <img src="../static/images/test.png" width="1000" th:src="#{images/test.png}"/>, which gives a blank image. Why isn't it showing up?
In my SecurityConfiguration.java file, I have
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
//.antMatchers("/**").hasRole("ADMIN")
.antMatchers("/static").permitAll()
.and().formLogin()
.loginPage("/login")
.permitAll();
When I use this configuration, it uses the default index.html page which shows the image fine. But, If I uncomment .antMatchers("/**").hasRole("ADMIN"), it will bring me to login.html, but I can't view the image.
There are 3 problems I can spot.
Instead of .antMatchers("/static") you should rather have .antMatchers("/images/**") since anything from src/main/resources/static will be served from the root of your application (as explained here - mind that folders "public" and "static" are interchangeable to Spring Boot).
Order of matchers for .authorizeRequests() matters! Just look as last example of method's documentation. You should have your ant matchers reversed:
.antMatchers("/images/**").permitAll() // more detailed paths should go first
.antMatchers("/**").hasRole("ADMIN") // more general paths should go last
Consider using th:src="#{/images/test.png}" instead of th:src="#{images/test.png}". The extra slash at beginning makes the path relative to the root of your application what gets along with first advice. As stated in Thymeleaf's documentation:
Relative URLs starting with / (eg: /order/details) will be automatically prefixed by the application context name.
Related
I followed this tutorial on using a content version strategy in spring for static assets. Everything works as intended except there is a corner case that I don't know how to fix:
My HTML has a <link> to a css file, a.css. If I look at the html returned by the server, I see that the link has been transformed to a-(md5).css, as it should. The problem I have is that a.css imports b.css. Spring is also properly updating the import from #import '/css/b.css' to #import '/css/b-(md5).css' The problem appears when I update b.css. Because the md5 of a.css is the same (the #import is to the static name), the browser is caching the request of a-(md5).css, which still points to the resolved b-(old-md5).css and I end up with the wrong styling
This sounds like a common problem. How can this be fixed?
Is it possible to tell the version strategy to compute the md5 after resolving links so that if the dependency's md5 changed, so would the dependent's md5?
This is my config
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//content-based versioning and max caching
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(MAX_CACHE_DURATION)
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
.addTransformer(new CssLinkResourceTransformer());
//no cache
registry.addResourceHandler("/*.html").setCacheControl(CacheControl.noCache());
}
I couldn't find a way to solve this cleanly. My workaround was to disable caching for all css
registry.addResourceHandler("/ui/css/**")
.addResourceLocations("classpath:/static/css/")
.setCacheControl(CacheControl.noCache())
.resourceChain(false)
.addResolver(new VersionResourceResolver());
Following https://www.learnrazorpages.com/razor-pages/routing and "Friendly Routes"
With this folder structure:
~Pages / (or "Areas" in last example)
External
PageA
Index.cshtml
Internal
PageB
Index.cshtml
And then I want to add these routes
services.AddMvc()
.AddRazorPages(options =>
{
// Even though above guide states this route it...
// throws exception for "External/PageA" so I have to set "/External/PageA"
options.Conventions.AddPageRoute("External/PageA", "/A");
// throws exception for "External/PageB" so I have to set "/External/PageB"
options.Conventions.AddPageRoute("Internal/PageB", "/B");
});
But all I'm getting is 404 not found. I've tried multilple combinations. I've even followed the Area setup and moved it to areas with the same structure inside the Area folder:
services.AddMvc()
.AddRazorPages(options =>
{
options.AllowAreas = true;
// Also tried "PageA" and "/PageA", "A" and "/A"
options.Conventions.AddAreaPageRoute("External", "/PageA", "/A");
// Also tried "PageB" and "/PageB", "B" and "/B"
options.Conventions.AddAreaPageRoute("Internal", "/PageB", "/B");
});
It's more like the whole convention setup is totally ignored since there has been 0% progress.
The only way that I can access the page is by writing the full folder name. But I don't want this. I want a cleaner routing structure than the folder structure which at the moment seems impossible.
Any suggestions - any ideas?
From the page you linked to:
The [AddPageRoute] method takes two parameters. The first is the relative path to the Razor page file without the extension
You are providing the path to the folder containing the page. You need to add "/Index" to the end of the first argument:
options.Conventions.AddPageRoute("/External/PageA/Index", "A");
The same is true when working with areas. You need to provide the relative path to the actual page, not the folder name:
options.Conventions.AddAreaPageRoute("External", "/PageA/Index", "/A");
This assumes a structure as follows:
Areas
External
Pages
PageA
Index.cshtml
I've got a simple controller which processes and serves an image.
The controller:
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public byte[] GetProcessedImageResource(#RequestParam Long imageID) {
Image image = someDAO.getImageById(imageID)
someBusinessObject.processImage(image);
return image == null ? null : image.getData();
}
#ModelAttribute
public void setSomeHeaders(HttpServletResponse response) {
response.setHeader("Content-Type", "image/png");
response.setHeader("isThisHowIAddHeaders", "yup");
}
How it's invoked:
This controller is used in two contexts:
Context one (which currently works perfectly - and which there is dozens of instances of all over the site): When I want to use a particular image on another page like:
<img id="imageHolder" src="getProcessedImageResource.ctl?imageID=12345"></img>
context two(which I'm having issues with): The user just goes directly to a URL by visiting, say:
www.foo.bar.com/blah/getProcessedImageResource.ctl?imageID=12345
in their browser(which naturally anyone can do once they've been shown the image)
What I'm seeing:
When I intercept the response on the client side, I see "isThisHowIAddHeaders", "yup".... but I also see "Content-Type", "text/html;charset=ISO-8859-1"
In context one this works fine - the image is displayed without issue
In context two, spring-magic seems to try to marshall this bytestream into a full web page - it throws in the default header and footer and displays the image as text.
this causes two problems:
The image is displayed as gibberish encoding instead of an image:
.....QhÓÇÆ#BüN+?G*?3gÐèºÁ³?¯¨pTXX*ýAçýëßÒ8"¸òÿWÏ®M¦ K©æìÉ 8*oÍÚþýÀTPõæAj?.?äq*¢ê# ?©¦Í¦÷'?¹7ì?ÞêNGçÓ~²? òò?^1?¥}Dê:Y\¨¸?e¸úÛ?ûðaZã??W1¢?¡?Aóë??ÓefSô ??[ÜbX1×ݵç ùõ¦*B?c]Güþ?p1¡{·Ò#?Áµ¡ãý·½?*+N?ø[´Ñ©ëÖÇÂÿ_~=yåü(À|¿ÔzÂê# ¾?tÙ?ÉýpIÒÿãîêGcª?Dz?Á#?òÇ]þ.8æíÍÆ¢ºqkX}?¯¿yÐÿ«íé?á?Eÿ?® §õ_?'?ºqcίõCý?»8z~}_J.....
This text is displayed - unescaped- directly to users - and some of these images are user-submitted. I'll be honest XSS problems are my biggest concern here...
Other things I've tried:
Returning a ResponseEntity instead of using #ResponseBody
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "image/png");
return new ResponseEntity<byte[]>(image.getData(), headers, HttpStatus.FOUND);
(and removing #ModelAttribute setSomeHeaders() )
Using 'setContentType' instead
headers.setContentType(MediaType.IMAGE_PNG);
forcing spring to serve only the image data, with no header/footer/decoration of any kind - still get text/html content type, still get encoded image
Intercepting the response and manually changing the content-type back to 'image/[everything I can think of]' - just to see if that's the only problem - I get an actual image this time - but completely garbled(It's supposed to be a flower by the by...) :
Questions
Any ideas what I'm doing wrong here? The image seems to display fine when it's shown as part of an tag...but when the user references it directly, and the image is served in isolation, the browser seems to refuse to display it in any sensible manner.
The issue seems to be Spring's content negotiation mechanism. When you return byte[] from controller method annotated with #ResponseBody, Spring will go through it's registered MessageConverters and find one that can convert byte[] to a content type requested by the browser in the Accept header (which, in your second case will not be an image, because of .ctl extension, but in first case, since URL is inside the image tag, browser will ask for an image).
Later Spring versions offer a method to specify what content type controller method produces via produces parameter in the annotation.
You may try to intercept Accept header and change it via filter in order to "fool" Spring's content negotiation (I do not recommend this, it's a hack), you may create a custom class that that represents an image (e.g. ImageBytes class that wraps byte[]) and a register a custom message converter for that class and then return the class from the controller. You may create a message converter for the Image class you use too that would behave similarly. Modifying the url with a proper extension might also work.
My recommendation is to upgrade Spring version or skip the Spring's content negotiation and message converters altogether and output the response directly:
#RequestMapping(method=RequestMethod.GET)
public void GetProcessedImageResource(#RequestParam Long imageID, HttpServletResponse response) {
Image image = someDAO.getImageById(imageID)
someBusinessObject.processImage(image);
response.setContentType(MediaType.IMAGE_PNG_VALUE);
FileCopyUtils.copy(image.getData(), response.outputStream)
}
EDIT
After a bit more research, Spring seems to have a BufferedImageMessageConverter too: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/BufferedImageHttpMessageConverter.html
It seems that this message converter is not registered by default. Another approach to the solution which may work would be to register this converter and return BufferedImage directly from the controller.
I have developed a project using this link: https://spring.io/guides/gs/serving-web-content/ I used maven to develop above project.
I have two html files under this. abc.html and xyz.html. To insert images in the html page, I have used the url like this:
<img src="https://example.com/pic_mountain.jpg" alt="Mountain View" style="width:304px;height:228px">
But I want to use an image file located in my server instead. I tried placing the file in the same directory of html file but its not working. I even tried giving full path but of no use. This is an ubuntu OS. Please help me out here. Is there any place where I can configure the base path or basically how to put an image from my local folder.
I want you to look into the Thymeleaf's documentation of Standard URL Syntax and specifically the context-relative and server-relative url patterns.
Context-relative URL:
If you want to link resources inside your webapp then you should use
context relative urls. These are URLs which are supposed to be
relative to the web application root once it is installed on the
server. For example, if we deploy a myapp.war file into a Tomcat
server, our application will probably be accessible as
http://localhost:8080/myapp, and myapp will be the context name.
As JB Nizet the following will work for you as I have used thymeleaf personally in a webapp project,
<img th:src="#{/images/test.png}"/>
and the test.png should be under your project root inside the webapp folder. Something navigated through roughly like,
Myapp->webapp->images->test.png
Eg:
<img th:src="#{/resources/images/Picture.png}" />
Output as:
<img src="/resources/image/Picture.png" />
When you hit http://localhost:8080/myapp/resources/images/Picture.png in you browser then you should be able to access the image for the above syntax to work. And your resources folder will probably under webapp folder of your application.
Server-relative URL:
Server-relative URLs are very similar to context-relative URLs, except
they do not assume you want your URL to be linking to a resource
inside your application’s context, and therefore allow you to link to
a different context in the same server
Syntax:
<img th:src="#{~/billing-app/images/Invoice.png}">
Output as:
<a href="/billing-app/showDetails.htm">
The above image will be loaded from an application different from your context and if an application named billing-app is present in your server.
Sourced from: Thymeleaf documentation
You need to understand how HTTP works. When the browser loads a page at URL
http://some.host/myWebApp/foo/bar.html
and the HTML page contains
<img src="images/test.png"/>
the browser will send a second HTTP request to the server to load the image. The URL of the image, since the path is relative, will be http://some.host/myWebApp/foo/images/test.png. Note that the absolute path is composed from the current "directory" of the page, concatenated with the relative path of the image. The path of the server-side JSP or thymeleaf template is completely irrelevant here. What matters is the URL of the page, as displayed in the address bar of the browser. This URL is, in a typical Spring MVC application, the URL of the controller where the initial request was sent.
If the path of the image is absolute:
<img src="/myWebApp/images/test.png"/>
then the browser will send a second request to the URL http://some.host/myWebApp/images/test.png. The browser starts from the root of the web server, and concatenates the absolute path.
To be able to reference an image, whetever the URL of the page is, an absolute path is thus preferrable and easier to use.
In the above example, /myWebAppis the context path of the application, that you typically don't want to hard-code in the path, because it might change. Thankfully, according to the thymeleaf documentation, thymeleaf understands that and provides a syntax for context-relative paths, which thus transforms paths like /images/test.png into /myWebApp/images/test.png. So your image should look like
<img th:src="#{/images/test.png}"/>
(I've never used thymeleaf, but that's what I deduce from the documentation).
And the test.png image should thus be in a folder images located under the root of the webapp.
Get link on Internet:
String src = "https://example.com/image.jpg";
HTML: <img th:src="#{${src}}"/>
I have used bellow like..
My image path is like bellow..
I have used bellow code for loading image
<img th:src="#{imges/photo_male_6.jpg}" >
It is working fine for me.
Recently I had similar issue, but in my case, the spring security was making a problem. As mentioned in other answers and documentation:
<img th:src="#{/img/values.png}" alt="Media Resource"/>
should be enough. But since the spring security has been added to my project, I had to all /img/ ** for get Requests and add addResourceHandlers. Here is the code:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(
"/webjars/**",
"/img/**",
"/css/**",
"/js/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/",
"classpath:/static/img/",
"classpath:/static/css/",
"classpath:/static/js/");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.authorizeRequests().antMatchers(HttpMethod.GET, "/js/**", "/css/**", "/img/**" ,"/pressiplus", "/public/**", "/index", "/", "/login").permitAll();
http.authorizeRequests()
.antMatchers("/secure/admin/**").hasAnyRole("ADMIN","USER")
.antMatchers("/secure/admin/**").hasAnyRole("ADMIN")
.and()
.formLogin() //login configuration
.loginPage("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/login")
.usernameParameter("email")
.passwordParameter("password")
.successHandler(myAuthenticationSuccessHandler())
.and()
.logout() //logout configuration
.logoutUrl("/logout")
.logoutSuccessHandler(myLogoutSuccessHandler)
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(7 * 24 * 60 * 60) // expires in 7 days
.and()
.exceptionHandling() //exception handling configuration
.accessDeniedHandler(accessDeniedHandler());
}
}
I hope this helps someone in the future
Who retrieve link dynamically use this pattern
<img class="image" th:src="#{'/resources/images/avatars/'+${theUser.avatar}}" alt="Avatar">
if you use like this (${theUser.avatar}) it will add ? in above version link look like this: /resources/images/avatars/photoname.png
As DimaSan said here https://stackoverflow.com/a/40914668/12312156
You should set image src like this:
<img src="../static/img/signature.png" th:src="#{img/signature.png}"/>
I'm creating a site using Magnolia - CMS. Now I am implementing a blog page. On each blog page, there are several share buttons. Now I'm busy implementing the twittershare button. Here I am going to use the twitter cards. For that, I need to provide the URL of an image in a metatag. Main problem: I retreive my image like this: ${damfn.getAssetLink(content.blogImage)}. This only returns a relative path to my resource. Is there a quick way (in freemarker), that will convert tis to an absolute link?
Many thanks in advance!
usually you define magnolia.default.base.url in the magnolia.properties.
then you can retrieve it with Components.getComponent(ServerConfiguration.class).getDefaultBaseUrl()
now you have to install the service into freemarker. you can do that by adding installer-tasks into the renderers on startup. you do that in your module-version-handler. there you overwrite the getStartupTasks(...), something like this:
#Override
protected List<Task> getStartupTasks(InstallContext installContext) {
final List<Task> tasks = new ArrayList<>();
tasks.add(new InstallRendererContextAttributeTask("rendering", "freemarker", "serverConf", ServerConfiguration.class.getName()));
tasks.add(new InstallRendererContextAttributeTask("site", "site", "serverConf", ServerConfiguration.class.getName()));
return tasks;
}
now you can call in freemarker:
"${serverConf.defaultBaseUrl}/${ctx.contextPath}/${damfn.getAssetLink(content.blogImage)}"
checkout if the slashes are necesarry and make sure that defaultBaseUrl is set properly in your magnolia configuration ("/server/...")
edit: there should be an easier by calling the current request in freemarker ${Request} so it could be something like "${Request.domain}/${ctx.contextPath}/${damfn.getAssetLink(content.blogImage)}" without injecting the serverConfiguration into the renderer