JSF2.0实战 - 9、自定义Filter处理资源文件依赖关系

截止目前,前面第6节遗留的问题还有一个没处理:

我们测试时用的URL全部是以http://.../faces/.../xxx.xhtml路径来访问的,如果以http//.../xxx.faces来访问就可以看到dojo.js没生效,这是相对路径不匹配造成dojo.js无法加载,这个问题也需要解决。

先看效果

这是用/faces访问的效果

JSF2.0实战 - 9、自定义Filter处理资源文件依赖关系_第1张图片

下面这是用.faces访问的效果

JSF2.0实战 - 9、自定义Filter处理资源文件依赖关系_第2张图片

可以看到用.faces访问时dojo.js没运行,这是因为两个原因:1、dojo.js没加载。2、<script>require(["dojo/parser", ...这段脚本依赖的js文件没有加载。

第1个原因通过查看两个页面的源代码就能发现,用/faces访问时,产生的代码是

<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css" />

而用.faces访问时,产生的代码是

<link type="text/css" rel="stylesheet" href="/dojo4j/test/textBoxTest.faces/javax.faces.resource/dojo/resources/dojo.css" />

明显的路径错误,解决这个问题只需修改Header.java,重写资源文件引用的代码,加入方法

private String getResourcePath(String resourceName) {
		String uri;
		FacesContext context = FacesContext.getCurrentInstance();
		String facesServletMapping = Util.getFacesMapping(context);
		// If it is extension mapped
		if (Util.isPrefixMapped(facesServletMapping)) {
			uri = facesServletMapping + ResourceHandler.RESOURCE_IDENTIFIER + '/' + resourceName;
		} else {
			uri = ResourceHandler.RESOURCE_IDENTIFIER + '/' + resourceName + facesServletMapping;
		}
		HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
		return request.getContextPath() + uri;
	}

把这几句

writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath()
	+ "/javax.faces.resource/dojo/resources/dojo.css", null);
writer.writeAttribute("href", request.getContextPath() + context.getExternalContext().getRequestServletPath()
	+ "/javax.faces.resource/dijit/themes/" + this.theme + "/" + this.theme + ".css", null);
writer.writeAttribute("src", request.getContextPath() + context.getExternalContext().getRequestServletPath()
	+ "/javax.faces.resource/dojo/dojo.js", null);

改为

writer.writeAttribute("href", getResourcePath("dojo/resources/dojo.css"), null);
writer.writeAttribute("href", getResourcePath("dijit/themes/" + this.theme + "/" + this.theme + ".css"), null);
writer.writeAttribute("src", getResourcePath("dojo/dojo.js"), null);

这样改了以后,用/faces访问时生成

<link type="text/css" rel="stylesheet" href="/dojo4j/faces/javax.faces.resource/dojo/resources/dojo.css" />

用.faces访问时生成

<link type="text/css" rel="stylesheet" href="/dojo4j/javax.faces.resource/dojo/resources/dojo.css.faces" />

这样就可以正常生成所需的链接代码。

尽管这样,但dojo在运行<script>require(["dojo/parser", ...时,并不能加载所依赖的脚本文件,通过Google Chrome浏览器调试,可以看到

JSF2.0实战 - 9、自定义Filter处理资源文件依赖关系_第3张图片

所依赖的parser.js、TextBox.js、Button.js无法下载,这是因为dojo在加载时这几个脚本文件时路径,用的是标准的.js结尾,但是这种情况JSF是不能正常处理的,必须要在路径后面加上.faces后缀,JSF才能正确读取资源文件

JSF2.0实战 - 9、自定义Filter处理资源文件依赖关系_第4张图片

解决这个问题只有用Filter。

Filter.java

package org.dojo4j.webapp;

import java.io.IOException;

import javax.faces.application.ResourceHandler;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sun.faces.util.Util;

public class DojoFilter implements Filter {

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
			ServletException {
		if (!(req instanceof HttpServletRequest) || !(resp instanceof HttpServletResponse)) {
	        throw new ServletException("DojoFilter just supports HTTP requests");
	    }
	    
	    HttpServletRequest request = (HttpServletRequest) req;
	    HttpServletResponse response = (HttpServletResponse) resp;
		
	    String uri = request.getRequestURI();
	    if (uri.contains(ResourceHandler.RESOURCE_IDENTIFIER)) {
			String facesServletMapping = getFacesMapping(request);
			//只处理facesServletMapping为.xxx后缀的情况,不处理facesServletMapping为/xxx为前缀的情况
			if (!Util.isPrefixMapped(facesServletMapping)) {
				//只有从引用资源的页面URL中才能取得Faces Servlet的后缀
				String referer = request.getHeader("Referer");
	        	if (referer != null && referer.trim().length() > 0) {
		        	if (referer.indexOf('?') > -1) {
		        		referer = referer.substring(0, referer.indexOf('?'));
		        	}
		        	if (referer.indexOf('.') > -1) {
		        		String subfix = referer.substring(referer.lastIndexOf('.'));
		        		
		        		//给没有Faces Servlet的后缀的资源加上后缀,确保通过JSF框架来解析资源文件
		        		if (!facesServletMapping.equals(subfix)) {
		        			String url = request.getServletPath() + subfix + (request.getQueryString() == null ? "" : "?" + request.getQueryString());
//		        			System.out.println(url);
		        			request.getRequestDispatcher(url).forward(request, response);
		        			return;
		        		}
		        	}
	        	}
	        	
			}
	    }
	    
	    chain.doFilter(req, resp);
	}
	
	private String getFacesMapping(HttpServletRequest request) {
		String servletPath = request.getServletPath();
        String pathInfo = request.getPathInfo();
        if (servletPath == null) {
            return null;
        }
        
        if (servletPath.length() == 0) {
            return "/*";
        }

        if (pathInfo != null) {
            return servletPath;
        } else if (servletPath.indexOf('.') < 0) {
            return servletPath;
        } else {
            return servletPath.substring(servletPath.lastIndexOf('.'));
        }
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
	}

	@Override
	public void destroy() {
	}
}

web.xml

<filter>
		<filter-name>DojoFilter</filter-name>
		<filter-class>org.dojo4j.webapp.DojoFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>DojoFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

JSF2.0实战 - 9、自定义Filter处理资源文件依赖关系_第5张图片

下载代码

到这里,开发简单的组件所需的技术就介绍得差不多了,我相信通过这些技术应该能够采用一些简单的js库来制作自己的JSF组件库了,如jquery ui、liger ui等。

后面我会写如何处理JSF ajax方式更新js组件,而不是html控件(下拉框联动),如何改造jsf.js实现同步请求(ajax表格取数、分页、排序),如何自定义dojo js控件来适应JSF组件开发的需要(树、表格、树表格),这些知识深入JSF和DOJO内部,比较复杂,但是对打造企业级的框架又必不可少,如果有兴趣请继续关注。

你可能感兴趣的:(框架,前端,JSF,dojo,JSF2.0)