Changing JSF prefix to suffix mapping forces me to reapply the mapping on CSS background images
and then I see that everything going through FacesServlet has .xhtml appended to it, so that the browser is requesting .png.xhtml files, .css.xhtml file - is this right?
This only applies to resources included by <h:outputStylesheet>
and <h:outputScript>
. This is not related to the change in the URL mapping. This is related to the change from JSF 1.x to JSF 2.x and the change from <link rel="stylesheet">
and <script>
to the aforementioned JSF2 tags.
For your own scripts, stylesheets and other static stuff which is to be served from the public webcontent, you should not manually add the .xhtml
extension. You should not need to change anything with regard to existing static resources.
Only for CSS background images and other url()
references in CSS files which is to be included using the <h:outputStylesheet>
tag (and thus not for <link rel="stylesheet>
), you would need to change the url()
location to be dynamically resolved by EL. You would need to use the following syntax instead:
body {
background-image: url("#{resource['libraryname:path/to/image.png']}");
}
Imagine that you have the following /resources
folder structure:
WebContent
|-- META-INF
|-- resources
| `-- default
| |-- images
| | `-- background.png
| `-- css
| `-- style.css
|-- WEB-INF
`-- test.xhtml
and that you're including the style.css
in test.xhtml
as follows
<h:outputStylesheet library="default" name="css/style.css" />
then you should be defining the background image URL as follows
body {
background-image: url("#{resource['default:images/background.png']}");
}
Or when you're relying on the default library, thus you aren't using the library
, then it should rather look like this:
WebContent
|-- META-INF
|-- resources
| |-- images
| | `-- background.png
| `-- css
| `-- style.css
|-- WEB-INF
`-- test.xhtml
test.xhtml
:
<h:outputStylesheet name="css/style.css" />
style.css
:
body {
background-image: url("#{resource['images/background.png']}");
}
As to the securiry constraint, it is not needed when you're already using the *.xhtml
mapping. The security constraint is intended to prevent the enduser from seeing the raw XHTML source code when the FacesServlet
is mapped on a pattern other then *.xhtml
. The enduser would be able to see the XHTML source code by just removing /faces
part from the URL in case of a /faces/*
mapping or renaming .jsf
to .xhtml
in case of a *.jsf
mapping. Get rid of the security constraint, it makes in your case things worse as you're already using a *.xhtml
mapping which makes it already impossible to see the raw XHTML source code by hacking the URL.
I have to add .xhtml to image in css
when .xhtml is appended to the end of a file the url is :
javax.faces.resource/...
while if you want to access a .js file directly for example the url is :
resources/script/myscript.js
don't forget the s in resources.
Prevent suffix from being added to resources when page loads
This is doable with a custom ResourceHandler
which returns in createResource()
a Resource
which in turn returns an "unmapped" URL on Resource#getRequestPath()
. You only need to add the default JSF resource prefix /javax.faces.resource/*
to the <url-pattern>
list of the FacesServlet
mapping in order to get it to be triggered anyway.
Further, you need to override isResourceRequest()
to check if the URL starts with the JSF resource prefix and also the handleResourceRequest()
to locate and stream the proper resource.
All with all, this should do:
public class UnmappedResourceHandler extends ResourceHandlerWrapper {
private ResourceHandler wrapped;
public UnmappedResourceHandler(ResourceHandler wrapped) {
this.wrapped = wrapped;
}
@Override
public Resource createResource(final String resourceName, final String libraryName) {
final Resource resource = super.createResource(resourceName, libraryName);
if (resource == null) {
return null;
}
return new ResourceWrapper() {
@Override
public String getRequestPath() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String mapping = externalContext.getRequestServletPath();
if (externalContext.getRequestPathInfo() == null) {
mapping = mapping.substring(mapping.lastIndexOf('.'));
}
String path = super.getRequestPath();
if (mapping.charAt(0) == '/') {
return path.replaceFirst(mapping, "");
}
else if (path.contains("?")) {
return path.replace(mapping + "?", "?");
}
else {
return path.substring(0, path.length() - mapping.length());
}
}
@Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
public String getResourceName() {
return resource.getResourceName();
}
@Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
public String getLibraryName() {
return resource.getLibraryName();
}
@Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
public String getContentType() {
return resource.getContentType();
}
@Override
public Resource getWrapped() {
return resource;
}
};
}
@Override
public boolean isResourceRequest(FacesContext context) {
return ResourceHandler.RESOURCE_IDENTIFIER.equals(context.getExternalContext().getRequestServletPath());
}
@Override
public void handleResourceRequest(FacesContext context) throws IOException {
ExternalContext externalContext = context.getExternalContext();
String resourceName = externalContext.getRequestPathInfo();
String libraryName = externalContext.getRequestParameterMap().get("ln");
Resource resource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);
if (resource == null) {
super.handleResourceRequest(context);
return;
}
if (!resource.userAgentNeedsUpdate(context)) {
externalContext.setResponseStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
externalContext.setResponseContentType(resource.getContentType());
for (Entry<String, String> header : resource.getResponseHeaders().entrySet()) {
externalContext.setResponseHeader(header.getKey(), header.getValue());
}
ReadableByteChannel input = null;
WritableByteChannel output = null;
try {
input = Channels.newChannel(resource.getInputStream());
output = Channels.newChannel(externalContext.getResponseOutputStream());
for (ByteBuffer buffer = ByteBuffer.allocateDirect(10240); input.read(buffer) != -1; buffer.clear()) {
output.write((ByteBuffer) buffer.flip());
}
}
finally {
if (output != null) try { output.close(); } catch (IOException ignore) {}
if (input != null) try { input.close(); } catch (IOException ignore) {}
}
}
@Override
public ResourceHandler getWrapped() {
return wrapped;
}
}
Register it as follows in faces-config.xml
:
<application>
<resource-handler>com.example.UnmappedResourceHandler</resource-handler>
</application>
Extend the FacesServlet
URL pattern with ResourceHandler.RESOURCE_IDENTIFIER
:
<servlet-mapping>
<servlet-name>facesServlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
<url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>
Background url path in a jsf template
CSS background images are loaded relative to the request URL of the CSS file (and thus not immediately relative to its physical location in the web content). If you explore the generated HTML output of the <h:outputStylesheet>
, then you'll see that the request URL has become different. Assuming a context path of /somecontext
and a FacesServlet
mapping of *.xhtml
, it'll look like this:
<link rel="stylesheet" type="text/css" href="/somecontext/javax.faces.resource/cssLayout.css.xhtml?ln=css" />
Note that your (improper btw) usage of the library
has moved the /css
to ?ln=css
. You'd need to fix the background image url()
accordingly so that it's properly relative to the real request URL of the CSS. E.g.
background-image: url("../resources/css/imgSite/sisLogo.png");
A more reliable way, which takes JSF resource identifier rules and FacesServlet
mapping into account, is to use #{resource}
in EL:
background-image: url("#{resource['css:imgSite/sisLogo.png']}");
See also:
- Changing JSF prefix to suffix mapping forces me to reapply the mapping on CSS background images
- What is the JSF resource library for and how should it be used?
Redirect CSS URL to one without www subdomain
Personally, I always use:
#{facesContext.externalContext.requestContextPath}/resources/cssname.css
As the path to any of my resources.
This way, I am sure that I am always getting the right path to my resource.
Hope this helps
Cheers
How to reference JSF image resource as CSS background image url
When importing CSS stylesheets by <h:outputStylesheet>
, the stylesheet is imported and processed by the FacesServlet
through /javax.faces.resource/*
. Look at the generated <link>
element pointing to the stylesheet in question and you'll understand.
You have to change all url()
dependencies to use #{resource['library:location']}
instead. JSF will then auto-substitute it with the right path. Given your folder structure, you need to replace
.c2 {
background: url("/resources/images/smiley.jpg");
}
by
.c2 {
background: url("#{resource['images/smiley.jpg']}");
}
Assuming that your webapp context name is playground
and that your FacesServlet
is mapped on *.xhtml
, then the above should end up in the returned CSS file as follows
.c2 {
background: url("/playground/javax.faces.resource/images/smiley.jpg.xhtml");
}
Noted should be that the JSF implementation will for determine only during the first request on the CSS file if it contains EL expressions. If it doesn't then it will for efficiency not anymore attempt to EL-evaluate the CSS file content. So if you add an EL expression to a CSS file for the first time, then you'd need to restart the whole application in order to get JSF to re-check the CSS file.
In case you wanted to reference a resource from a component library such as PrimeFaces, then prefix the library name, separated with :
. E.g. when you're using PrimeFaces "Start" theme which is identified by primefaces-start
.c2 {
background: url("#{resource['primefaces-start:images/ui-bg_gloss-wave_50_6eac2c_500x100.png']}");
}
This will be generated as
.c2 {
background: url("/playground/javax.faces.resource/images/ui-bg_gloss-wave_50_6eac2c_500x100.png.xhtml?ln=primefaces-start");
}
See also:
- How to reference CSS / JS / image resource in Facelets template?
- Changing JSF prefix to suffix mapping forces me to reapply the mapping on CSS background images
Unrelated to the concrete problem, the way how you use the library
is not entirely right. It's meant to be the common identifier/subfolder of all related CSS/JS/image resources. The key idea is to be able to change the entire look'n'feel by just changing the library
(which can be done by EL). You seem however to be relying on the default library. In that case, you could just omit the library
from your <h:outputStylesheet>
and #{resource}
.
<h:outputStylesheet name="css/style.css" />
See also:
- What is the JSF resource library for and how should it be used?
Why does UnmappedResourceHandler require a servlet mapping for JSF resource URL prefix pattern?
JSF resources also need to be served by the FacesServlet
. It's the one responsible for locating the right file in /resources
folder and returing it along with proper caching headers. If you remove the .xhtml
extension (or /faces
path) from the resource URL, then the URL won't match the URL pattern of FacesServlet
anymore and thus the FacesServlet
wouldn't be invoked and wouldn't be able to do its job of serving the resource. You would only get a 404 Not Found error back, because the "raw" resource URLs do not match the public webcontent folder structure.
UnmappedResourceHandler
makes use of the fact that JSF resource URLs have a common prefix path /javax.faces.resource
, as identified by ResourceHandler.RESOURCE_IDENTIFIER
. So, in order to invoke the FacesServlet
anyway, even without the .xhtml
extension (or /faces
path), you just need to add the /javax.faces.resource/*
URL pattern to the mapping.
The CDNResourceHandler
doesn't need a change in the mapping as it doesn't produce JSF resource URLs anyway, but true CDN URLs like ones pointing to jQuery or Google CDN hosts. Those URLs wouldn't (need to) hit the FacesServlet
anyway.
The CombinedResourceHandler
just produces default JSF resource URLs, with .xhtml
extension (or /faces
path). It only uses a special library
name of omnifaces.combined
, so that the CombinedResourceHandler
can identify them.
Related Topics
Transparent Rounded Corners on Google Map
Multiple Font-Weights, One @Font-Face Query
Css: Using Raw Svg in the Url Parameter of a Background-Image in Ie
Background Images: How to Fill Whole Div If Image Is Small and Vice Versa
CSS "Outline" Different Behavior Behavior on Webkit & Gecko
How to Use Safe Center with Flexbox
How to Apply a Style to All Children of an Element
Inner Border Over Images with CSS
In CSS, Differencebetween Cascading and Inheritance
Correct Terms and Words for Sections and Parts of Selectors
How to Apply Multiple Transform Declarations to One Element
How to Compile or Convert SASS/Scss to CSS with Node-Sass (No Ruby)
What Does .Container.\31 25\25 Mean in CSS
How to Create the Masonry Effects with Just Bootstrap 3 Grid System and CSS
Faster Way to Develop and Test Print Stylesheets (Avoid Print Preview Every Time)
MVC Bundling and CSS Relative Urls