转载http://blog.csdn.net/yeahwell/article/details/8547243
最近想自己写一个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
4、WEB-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>还是这这样.
6、WEB-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
最近想自己写一个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
4、WEB-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>还是这这样.
6、WEB-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