J2EE中JSP页面引用外部资源时的路径问题

    最近想自己写一个cms系统,在前台View层展示上,初始版本用jsp,以后用Freemarker优化,包括生成静态页面等。在设计JSP页面时,遇到了最头疼的问题。路径问题。

   J2EE学习道路上有两个最为恼火的问题,一是编码问题,二就是路径问题。PHP中通常定义一个全局常量,包含在公共php文件中,每次都会被其他页面引用。JSP在这个方面确实不如PHP,这也是MVC架构的一个弊端。

    我采用的框架是Springmvc+Spring+Hibernate+SpringJDBCTemplate。

   网上资料收集。

一、绝对路径与相对路径

绝对路径: 如果咱们使用的URL网址是以“/”开头的,那么这个网址就叫做绝对路径。

相对路径: 如果咱们使用的URL网址不是“/”开头的,那么这个网址就叫做相对路径。

1.  相对路径

在相对路径上,两者的表现是相同的。

看看lingo-sample/03-03/这个例子,如果我们去请求relative/forward.jsp或redirect.jsp,然后从这里再跳转向它下面的result/result.jsp会怎样呢?

1.1  forward中使用相对路径

forward的例子:

<%request.getRequestDispatcher("result/result.jsp").forward(request,response);%>

这里的相对路径就是result/result.jsp。

因为刚刚请求的test.jsp是在/03-03/relative/下,所以我们的当前路径就是/03-03/relative/,执行forward的时候会寻找当前路径下的result/result.jsp,找到之后便转发请求。

1.2  redirect中使用相对路径

redirect的例子:

<%response.sendRedirect("result/result.jsp");%>           

这里的相对路径也是result/result.jsp。

因为刚刚请求的test.jsp是在/03-03/relative/下,所以我们的当前路径就是/03-03/relative/,执行 redirect的时候会把当前路径加上result/result.jsp,把结果作为重定向的地址发送给浏览器,浏览器再去请求/03-03 /relative/result/result.jsp,从而得到响应。

2. 绝对路径

问题出现了,绝对路径在forward和redirect中出现了差别,还是刚才的情况,但使用绝对路径的时候写法便不同了。

2.1  forward中使用绝对路径

forward的例子:

<%request.getRequestDispatcher("/relative/result/result.jsp").forward(request,response);%>这里的绝对路径就是/relative/result/result.jsp。

在本地测试时,forward把http://localhost:8080/03-03/当作根路径,在它的基础上计算绝对路径。

这是由jsp的部署方式决定的,webapp里可以放好多项目,为了让这些项目可以互不影响、独立运行,不能让请求从一个项目直接在服务器内部转移到另一个项目。为了防止出现这种情况,在执行forward的时候干脆把项目的路径当作根目录,开发者看不到其他项目,也就不会出现问题了。

2.2  redirect中使用绝对路径

redirect的例子:

<%response.sendRedirect("/03-03/absolute/result/result.jsp");    这里的绝对路径却是/03-03/absolute/result/result.jsp。

在本地测试时,redirect把http://localhost:8080/当作根路径,在它的基础上计算绝对路径。

因为redirect会让浏览器重新发起一个新请求,所以不会搅乱服务器里多个项目之间的关系,也就不需要对它做限制,如果需要在多个项目之间进行跳转,就只能使用redirect。不过因为重新发起了新的请求,上次请求的那些数据都会丢失,如果有什么重要的数据,记得要重新设置。

3. forward后寻找不到资源

forward导致找不到资源

找不到图片,找不到js脚本,找不到css样式表,都属于这个问题。

要演示这个问题,是非常容易的,只需要满足两个条件:

(1) forward前后的jsp页面不在一个目录下。

(2) forward后的jsp页面里使用相对路径引用一些资源,图片,js脚本,css样式表什么的。

03-04里就模拟了这样一个环境,你进入http://localhost:8080/03-04/,选择“有问题的”: 

打开03-04可以看到如下的目录结构:

|--+ 03-04

   |--- index.jsp

   |--- test.jsp

   |--+ result

     |--- success.jsp

     |--- failure.jsp

     |--- lingo.png

           

刚才咱们看到的页面是failure.jsp,它里边显示图片的部分是:

<img src="lingo.png" />        

这时候就有疑问了,lingo.png和failure.jsp明明在同一个目录下,为什么无法显示。

现在请在无法显示的图片上,点击鼠标右键,选择属性,让我们看一下图片的请求地址: 

图片的位置本来在http://localhost:8080/03-04/result/lingo.png,但请求的地址却是http://localhost:8080/03-04/lingo.png。问题就是丢掉了中间的/result。

再试一次index.jsp上的“没问题的”: 

这次我们看到的页面是success.jsp,它里边显示图片的部分是:

<img src="result/lingo.png" />

结果手工加上result这段路径后就可以显示图片了。

这个问题还要追溯到浏览器对html的处理方式,在html里包含的图片,css样式表,js脚本,视频等等外部资源,都需要浏览器再次向服务器发起请求。

如果这些外部资源使用了相对路径,浏览器就会在当前请求路径的基础上,加上相对路径拼接出完整的http请求,发送给服务器。这个例子中,我们请求 http://localhost:8080/03-04/test.jsp,浏览器得到的当前路径就是http://localhost:8080 /03-04/failure.jsp中图片的相对路径是lingo.png,那么拼接的结果是http://localhost:8080/03-04/lingo.png。

不要怪浏览器太傻,是因为使用forward的时候浏览器并不清楚这些改变。它一直认为,既然自己请求的是test.jsp,返回的自然就是 test.jsp的内容,那么再使用test.jsp当作当前路径去计算相对路径当然没有问题。是我们欺骗了浏览器,在服务器偷偷改变了请求流向,返回了其他页面的内容。

清楚了以上的请求流程,就知道如何应对这种问题了。

(1)  第一种方法:不要在不同目录之间使用forward做请求转发,保证当前路径不发生变化。

(2)  第二种方法:像上例一样修改图片路径,或全部改为绝对路径。

请根据实际需要进行选择。


二、 Servlet内部跳转和外部跳转的区别,底层实现原理

Servlet页面跳转分两部分,一是发生在Servlet,一是在JSP,其实JSP也就是servlet,不过还是有点差异。

当然,在servlet中,一般跳转都发生在doGet,doPost等方法里面。

1  原理

(1) redirect方式(客户端跳转)

response.sendRedirect("/a.jsp");

页面的路径是相对路径。sendRedirect可以将页面跳转到任何页面,不一定局限于本web应用中,如:

response.sendRedirect("http://www.ycul.com");

跳转后浏览器地址栏变化。

这种方式要传值出去的话,只能在url中带parameter或者放在session中,无法使用request.setAttribute来传递。

这种方式是在客户端作的重定向处理。该方法通过修改HTTP协议的HEADER部分,对浏览器下达重定向指令的,让浏览器对在location中指定的URL提出请求,使浏览器显示重定向网页的内容。该方法可以接受绝对的或相对的URLs。如果传递到该方法的参数是一个相对的URL,那么Web container在将它发送到客户端前会把它转换成一个绝对的URL。

public void doPost(HttpServletRequestrequest,HttpServletResponse response) throws ServletException,IOException {

       response.setContentType("text/html; charset=UTF-8");

       response.sendRedirect("/index.jsp");

}

 

(2) forward方式(服务器端跳转)

RequestDispatcher dispatcher =request.getRequestDispatcher("/a.jsp");

dispatcher .forward(request, response);

页面的路径是相对路径。forward方式只能跳转到本web应用中的页面上。

跳转后浏览器地址栏不会变化。

使用这种方式跳转,传值可以使用三种方法:url中带parameter,session,request.setAttribute

这种方式是在服务器端作的重定向。

服务器往client发送数据的过程是这样的:服务器在向客户端发送数据之前,是先将数据输出到缓冲区,然后将缓冲区中数据发送给client端。什么时候将缓冲区里的数据发送给client端呢?

(1)当对来自client的request处理完,并把所有数据输出到缓冲区。

(2)当缓冲区满。

(3)在程序中调用缓冲区的输出方法out.flush()或response.flushbuffer(),web container才将缓冲区中的数据发送给client。

这种重定向方式是利用服务器端的缓冲区机制,在把缓冲区的数据发送到客户端之前,原来的数据不发送,将执行转向重定向页面,发送重定向页面的数据,重定向调用页的数据将被清除。

如果在<jsp:forward>之前有很多输出,前面的输出已使缓冲区满,将自动输出到客户端,那么这种重定向方式将不起作用,这一点应该特别注意。

 

public void doPost(HttpServletRequest request,HttpServletResponseresponse)   throwsServletException,IOException{

       response.setContentType("text/html; charset=UTF-8");

       ServletContext sc = getServletContext();

       RequestDispatcher rd = null;

       rd = sc.getRequestDispatcher("/index.jsp");

       rd.forward(request, response);

}

 

2、区别.

 

(1)forward重定向是在容器内部实现的同一个Web应用程序的重定向,所以forward方法只能重定向到同一个Web应用程序中的一个资源,重定向后浏览器地址栏URL不变,而  sendRedirect方法可以重定向到任何URL, 因为这种方法是修改http头来实现的,URL没什么限制,重定向后浏览器地址栏URL改变。

(2)forward重定向将原始的HTTP请求对象(request)从一个servlet实例传递到另一个实例,而采用sendRedirect方式两者不是同一个application。

(3)基于第二点,参数的传递方式不一样。forward的form参数跟着传递,所以在第二个实例中可以取得HTTP请求的参数。sendRedirect只能通过链接传递参数,response.sendRedirect(“login.jsp?param1=a”)。

(4)sendRedirect能够处理相对URL,自动把它们转换成绝对URL,如果地址是相对的,没有一个‘/’,那么Web container就认为它是相对于当前的请求URI的。

比如,如果为response.sendRedirect("login.jsp"),则会从当前servlet的URL路径下找login.jsp:http://localhost:8080/......重定向的URL:http://localhost:8080/....../....../login.jsp,    

如果为response.sendRedirect("/login.jsp")则会从当前应用径下查找url:http://localhost:8080/login.jsp。而forward不能这样处理相对路径。

java 他们的区别是:

response.sendRedirect是向客户浏览器发送页面重定向指令,浏览器接收后将向web服务器重新发送页面请求,所以执行完后浏览器的url显示的是跳转后的页面。跳转页面可以是一个任意的url(本服务器的和其他服务器的均可)。RequestDispatcher.forward则是直接在服务器中进行处理,将处理完后的信息发送给浏览器进行显示,所以完成后在url中显示的是跳转前的页面。

在forward的时候将上一页面中传送的request和response信息一同发送给下一页面(而response.sendRedirect不能将上一页面的request和response信息发送到下一页面)。由于forward是直接在服务器中进行处理,所以forward的页面只能是本服务器的。

 

 

JSP:

1) response.sendRedirect();

和servlet的response.sendRedirect()方式一样。

此语句前不允许有out.flush(),如果有,会有异常:

java.lang.IllegalStateException: Can'tsendRedirect() after data has committed to the client.

atcom.caucho.server.connection.AbstractHttpResponse.sendRedirect(AbstractHttpResponse.java:558)

...

跳转后浏览器地址栏变化

如果要跳到不同主机下,跳转后,此语句后面的语句会继续执行,如同新开了线程,但是对response的操作已经无意义了;

如果要跳到相同主机下,此语句后面的语句执行完成后才会跳转;

 

2)response.setHeader("Location","");

此语句前不允许有out.flush(),如果有,页面不会跳转。

跳转后浏览器地址栏变化

此语句后面的语句执行完成后才会跳转

 

3) <jsp:forward page="" />

此语句前不允许有out.flush(),如果有,会有异常:

java.lang.IllegalStateException: forward()not allowed after buffer has committed.

atcom.caucho.server.webapp.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:134)

atcom.caucho.server.webapp.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:101)

atcom.caucho.jsp.PageContextImpl.forward(PageContextImpl.java:836)

...

跳转后浏览器地址栏不变,但是只能跳到当前主机下

此语句后面的语句执行完成后才会跳转

 

三、java 路径(相对路径和绝对路径)问题汇总   

http://javawebsoa.iteye.com/blog/1512804

 

四、jsp 中include的外部jsp文中的css为什么不生效?

问题:

jsp 中include的外部jsp文中的css为什么不生效?
我在一个index.jsp文件中include了两个文件(header.jsp 、footer.jsp)为什么这两个文件中的css没有生效呢?单独测试是正常的!!
在index。jsp中我是这样写的:
<%@ page language="java" import="java.util.*"pageEncoding="gb2312"%>
<html>
  <head>   
    <title>TonyBoy</title>
 <!--
 <link rel="stylesheet"type="text/css" href="styles.css">
 -->
  </head>
 
  <body>
   <jsp:includepage="pages/head.jsp"></jsp:include>
   <br>
    This is my JSP page BODY
     <br>
    <jsp:includepage="pages/foot.jsp"></jsp:include>
  </body>
</html>

答案:

被引用的jsp中引入的css文件也是以引用页的目录为根目录的,就是说引用页(head.jsp和foot.jsp)是以你的index.jsp所在的目录为根目录的,所以你也要在被引用页(head.jsp和foot.jsp)中引用css的语句中的源文件前面加上"pages/"

 

五、关于jsp页面是放在WebRoot(Eclipse中为WebContent)目录下和WEB-INFO下优缺点,该怎么处理的讨论

讨论:关于jsp页面是放在webroot目录下和web-inf下优缺点

jsp放在webroot目录下 这样就可以让用户直接访问,jsp放在web-inf目录下就必须要通过请求才能访问。

因此放在web-inf下jsp页面显得要安全。

既然这样,那是不是只要是需要通过请求才可以访问的页面就一定要放在web-inf目录下呢

还有放在webroot和放在web-inf目录下都各有什么明显的优缺点。

 

望大家讨论...


------解决方案--------------------------------------------------------

关注中。。。

------解决方案--------------------------------------------------------

通过设置过滤器,放在webroot下面的文件也可以实现不能直接访问。所以说放在哪里就看习惯是什么样了。

一般项目都是要求隐藏性的,只让客户通过请求访问而不是直接访问jsp页面。若放在webroot下面,肯定要加一个过滤器阻止所有对*.jsp的访问。只要比较的话:

放在webroot下面:优点,程序结构清晰,便于编码和维护;缺点,要加过滤器。

放在web-inf下面:优点,不用过滤器;缺点,打乱了程序结构,编码和维护麻烦点。

 

其实二者没啥大区别,个人倾向于放在webroot下面

------解决方案--------------------------------------------------------

如果把这些JSP页面文件移到WEB-INF 目录下,在调用页面的时候就必须把"WEB-INF"添加到URL中。我们知道,实现页面的跳转有两种方式,一种是通过redirect的方式,一种是通过forward的方式。redirect方式的跳转,系统会在一个新的页面打开要跳转的网页;而forward方式跳转,系统会在原来的页面上打开一个要跳转的网页。所以放到WEB-INF目录下的文件是不允许采用redirect方式的跳转来访问的

如下例1:/test/test1.jsp文件

<html>

    <body>

    <form name="testform"action="/WEB-INF/jsp/test/test.jsp">

       <input type ="submit" value="test">

   </form>

   </body>

</html>

    上面这段语句只有一个名为test的按钮,如果单击这个按钮是,系统就会跳转到/WEB-INF/jsp/test/test.jsp,它的代码如下:

例2:/WEB-INF/jsp/test/test.jsp文件

<html>

   <body>

    跳转成功!

  </body>

</html>事实上,这个跳转是无法成功的,点击按钮后,IE会报“403 Forbidden”的错误。

而forward方式的跳转则可以成功,如下代码:

例3:/test/test2.jsp文件

<html>

  <body>

  <form name="testform">

     <jsp:forward page ="/WEB-INF/jsp/test/test.jsp" />

    </form>

   </body>

</html> 请注意上面红色的语句,这段就是通过forward的形式来访问/WEB-INF/jsp/test/test.jsp文件,在IE输入地址http://localhost/test1/test2.jsp,网页上就显示“跳转成功!”的信息了,这表示放到了WEB-INF可以通过forward的方式来访问。个人认为,像这种方式的可能不大时候采用一般jsp进行编程的系统,因为很多页面上都有采用submit这样的方式来进行跳转,但这种方式却非常适合采用struts结构的系统。因为采用这个结果大多是先跳转到一个Action类,然后在Action类进行相关处理后(比如说获取相关的信息保存到session中,进行有效性的判断),然后再forward到另外一个页面,这样放到WEB-INF中的jsp代码可以被正常访问,也防止了对这些页面的直接访问

下面我来举例说明。下面我们先对配置文件struts-config.xml进行配置,如下:例4:WEB-INF/struts-config.xml文件 <?xmlversion="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPEstruts-config PUBLIC "-//Apache Software Foundation//DTD StrutsConfiguration 1.1//EN""http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"><struts-config><!--========== Action Mapping Definitions ==============================--><action-mappings><action path="/test" type=" test.TestAction"scope="request"> <forward name="test" path="/WEB-INF/jsp/test/test.jsp"/></action> </action-mappings></struts-config> 上面这个配置非常简单,这里定义了一个action类,它的路径为/test,所对应的类为test.TestAction.java,它都一个跳转页面,别名为test,对应的页面为/WEB-INF/jsp/test/test.jsp。 下面我们对例1的内容进行修改,使其跳转到/test去。例5:修改后的/test/test1.jsp文件<html><body> <form name="testform"action="/test"> <input type = "submit"value="test"> </form></body></html> 这样我们在IE中访问http://localhost/test/test1.jsp,然后点击test按钮,页面就会跳转到test.TestAction.java这个类来,下面是这个类的内容。package test;import javax.servlet.http.*;import org.apache.struts.action.ActionMapping;importorg.apache.struts.action.Action;importorg.apache.struts.action.ActionForm;importorg.apache.struts.action.ActionForward;public class TestAction extendsAction{public ActionForward perform(ActionMapping mapping, ActionForm form,HttpServletRequest req, HttpServletResponse res) { returnmapping.findForward("test"); }} 可以看到,这个类是继承Action类的,所有的控制类都必须继承Action类,这个类里面有一个perform方法,跳转到这个类都是从这个方法进行访问的(新版本可以是execute方法),现在这个方法里面只有一条语句,这句话的意思就是跳转到一个别名为test的页面,也就是/WEB-INF/jsp/test/test.jsp页面,这样我们点击test按钮后,IE就会显示“跳转成功!”这条信息,这表示系统允许这样的跳转。注意:CSS文件要存放在根目录,此时引用要用绝对路径!!!引用格式如:/项目名/css文件夹/*.css

标准的MVC模型建议把JSP放在WEB-INF下!

 

六、WEB-INF 有关的目录路径问题总结

 

1、资源文件只能放在WebContent下面,如 CSS,JS,image等.放在WEB-INF下引用不了.

 

2、页面放在WEB-INF目录下面,这样可以限制访问,提高安全性.如JSP,html

 

3、只能用转向方式来访问WEB-INF目录下的JSP,不用采用重定向的方式请求该目录里面的任何资源.如图:index.jsp >>  main.jsp

 

4WEB-INF目录下文件访问资源文件时,可以忽略WEB-INF这一层目录.如main.jsp要用css目录里的一个css文件.

    <link rel="stylesheet"type="text/css" href="css/comm201005faa3.css"/>这样就行了,从客户端的地址可以看出来

    服务器转向main.jsp就是在webroot下面.所以main.jsp和css目录可以讲是同一级目录.

5、WEB-INF/oa目录下访问images目录.怎么办呢.<img alt="" src="images/instpage.gif"></body>还是这这样.

6WEB-INF目录下的文件之间如何访问呢.如在main.jsp用<a href="oa.do">测试OA的路径</a>访问
   像main.jsp有10处链接到WEB-INF目录下的其它页面.那就得有10个转向Action.这个可以用DispatchAction类加参数专门处理转向工作.

  注:

转向方式: forward

      如struts-config文件中配置<forwardname="success" path="/WEB-INF/main.jsp" /> 或在Action中写request.getRequestDispatcher("/WEB-INF/main.jsp").forward(request, response);都是服务器读取了该页面内容,并发送到客户端.客户端的地址不变.内容跳转了

重定向方式: Redirect

     如struts-config文件中配置<forwardname="success" path="/WEB-INF/main.jsp"redirect="true"/>

或在action中response.sendRedirect("/error.jsp");重定向的含义就是服务器把地址发给客户端,让客户端去访问.这种办法显然针对WEB-INF目录是无用功.

 

七.    关于使用绝对路径还是相对路径

 

其实你自己也知道不可能。

这里的所有讨论只不过都是亡羊补牢而已,楼主的代码写法已经错了,现在只是怎么弥补,我记得我第一天去第一家公司,当时的上司就教导我,所有链接要么用相对路径,比如“index.html”,不关你是ROOT下还是其他APP下,绝对正确,因为是相对于当前路径而言的。要么就用绝对路径,但绝对路径必须使用<c:url value="XXX"/>,这句话的目的其实也就是多加了一个<%=request.getContextPath()%>。

 

不过根据我的经验我们最好使用相对路径,因为WEB常常会涉及到JS来操作链接的问题,而<C:url 只能处理预先需要编译的,不能处理AJAX即时获得的路径,所以我数据库里图片存储路径一般是"file/user/*",而不是"/file/user/*",如果使用后者,就会出现楼主一样的问题。

 

楼主回头是岸吧,你这种方法已经错了,所以不必去为一种错误的写法花费太多的时间,只要能解决目前的问题就行了,剩下的就是好好总结一下以后如何避免现在的问题。

 

 

八.   绝对路径解决方式1,所以的都是绝对

[html] view plaincopyprint?

 

   <span style="font-size:18px;">    <%@ page language="java"import="java.util.*" pageEncoding="GB18030"%>   

       <%   

       String path = request.getContextPath();   

       String basePath =request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";   

       %>   

           

       <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">   

       <html>   

         <head>   

           <base href="<%=basePath%>">   

               

           <title>My JSP 'index.jsp' starting page</title>   

           <meta http-equiv="pragma"content="no-cache">   

           <meta http-equiv="cache-control"content="no-cache">   

           <meta http-equiv="expires" content="0">       

            <metahttp-equiv="keywords"content="keyword1,keyword2,keyword3">   

           <meta http-equiv="description" content="This is mypage">   

           <!--  

           <link rel="stylesheet" type="text/css"href="styles.css">  

           -->   

         </head>  </span> 

 

把你的jsp页面中的所有链接前面都加上basePath,而这个basePath是什么呢,打印出来即可,一般是http://localhost:8080/工程名/

 

也可以只设置部分路径。

 

使用request.getContextPath()获取路径。

<imgsrc="<%=request.getContextPath()%>/image/aa1.gif"/>

 

 

九.    freeemarker中使用绝对路径

 

部采用绝对定位即可,比如我的使用,在一个全局macro定义中定义变量basePath即可

 

首先定义公共宏文件app.ftl

Ftl代码

 

[html] view plaincopyprint?

 

   <span style="font-size:18px;">    <#-- 取得 应用的绝对根路径 -->   

       <#assign basePath=request.contextPath>   

       <#macro head>   

                <title>预祝成功</title>   

                <link rel="shortcuticon" href="${basePath}/favicon.ico">   

                <link rel="icon"type="image/gif"href="${basePath}/animated_favicon.gif">   

                <metahttp-equiv="content-type" content="text/html;charset=UTF-8">   

                <metahttp-equiv="pragma" content="no-cache">   

                <metahttp-equiv="cache-control" content="no-cache">   

                <metahttp-equiv="expires" content="0">   

       </#macro> </span> 

如果是在<body>中还需要这个绝对路径那应该怎么办呢?我现在的想法是,在生成HTML的Action中直接用HttpServletRequest获取到绝对路径path,放到root中,然后在ftl页面中使用<base href="path">

所以在其他ftl文件中可以使用他了,比如在home.ftl中:

 

   <span style="font-size:18px;">    <#import "/common/app.ftl" asapp>    

       <base href="${app.basePath}/">  </span> 

 

十、Spring MVC+Freemarker+Javascript的多语言(国际化i18n/本地化)和主题(Theme)实现

http://www.cnblogs.com/Mainz/archive/2012/08/04/2622858.html

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页