Servlet & ServletRequest ServletResponse & HttpServletRequest HttpServletResponse
一、Servlet
我们平时说的Servlet
看上去很神秘,其实本质很简单,就是一个Java接口interface
而已。
下面是javax.servlet
的具体实现,只有几句代码:
public interface Servlet { public void init(ServletConfig config) throws ServletException;//初始化 public ServletConfig getServletConfig();//获得配置 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;//服务 public String getServletInfo();//获得信息 public void destroy();//销毁 }
这个接口只有五个方法,而且看到这些方法的名字,大家也大概能猜出来这个方法是干什么的了。
这其中的service(ServletRequest req,ServletResponse res)
方法,是Servlet的关键所在,是我们甚至可以说,其他方法”并不重要“,可以不进行实现。
这个方法的作用,是把Tomcat(Servlet容器)传入的请求进行处理,并让Tomcat知道向客户端发送怎样的回应。
那么客户端通过 Socket 传来的请求是如何变成ServletRequest
类的呢?这就是容器 (如:Tomcat)的作用了,容器把客户端的消息解析,封装成ServletRequest
,交给Servlet处理。
那么,问题就来了:
- Servlet容器怎么实现?HOW?
- 为什么要有Servlet?WHY?
我们举个例子来说明。
从原理上讲,Servlet可以处理任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器,所以我们就假设这样一个场景:
某大学大二学生的计算机网络实验课要求是自己实现一个HTTP服务器。
胡斌同学写了如下代码:
ServerSocket ss = new ServerSocket();//创建服务器 Socket socket = ss.accept();//监听客户端连接 /* 读取客户端数据 ............. */ OutputStream soutput = socket.getOutputStream(); soutput.write(("HTTP/1.1 404 Not Found\r\n"+ "Content-Type: text/plain\r\n"+ "Content-length: 10\r\n\r\n"+ "Not Found\r\n").getBytes()); soutput.flush();
总而言之,不管客户端发来什么消息,胡斌同学的服务器都告诉客户端:404 Not Found。(搞笑)
第二天,他发现,这样发送的消息是写死的,耦合性非常高,这并不是一个好事。
胡斌同学于是改写代码,抽象出一个Response类:
Response:
class Response{ private String res; private OutputStream os; public Response(OutputStream os){ this.os = os; } public void end(){ os.flush(); } public OutputStream getOutputStream(){ return os; } }
他是这样调用的:
/* 读取客户端数据 ............. */ OutputStream soutput = socket.getOutputStream(); Response res = new Response(soutput); OutputStream out = res.getOutputStream(); out.write(("HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/plain\r\n" + "Content-length: 10\r\n\r\n" + "Not Found").getBytes()); res.end();
这样我们就给Response自己设置内容。
第三天,老师说,我给你们一个类,你们谁能最快把这个类放到你们代码中,完美运行,谁就能获得满分。
老师就给了一个叫TeacherServlet
的类,这个类,是这么写的:
class TeacherServlet implements Servlet{ @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { OutputStream out = res.getOutputStream(); out.write(("HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/plain\r\n" + "Content-length: 10\r\n\r\n" + "Not Found").getBytes()); } @Override public void init(ServletConfig config) throws ServletException {} @Override public ServletConfig getServletConfig() {return null;} @Override public String getServletInfo() {return null;} @Override public void destroy() {} }
胡斌一看,wc!简直是抄的我的代码,这太好改了啊!!
于是他这么写:
/* 读取客户端数据 ............. */ OutputStream soutput = socket.getOutputStream(); Servlet servlet = new TeacherServlet(); ServletResponse res = new ServletResponse(soutput); servlet.service(null, res); res.end();
可以看到,他把Response
类重命名为了ServletResponse
,老师的代码只是给他原来的代码多了一层封装,他交的最快,所以他得了满分。
那么这里,胡斌同学写的这些代码,就叫一个“Servlet容器”。
那么两个问题中的一个,即Servlet容器怎么实现的问题,就解决了,
tomcat虽然比这个复杂的多,但是原理真的是和胡斌同学的代码是一样的,就是一个回调(callback)。
但是,这可远远不是一个符合标准的Servlet容器。
首先,他的ServletResponse
缺少很多必要的方法;其次,他看到老师没有调用ServletRequest
的方法, 就把req的值传为null
,这可是真的非常投机取巧了。
其实这个故事里,胡斌的老师其实是程序员的作用,老师可以开发Servlet,来给用户使用。
而胡斌写的程序相当于Tomcat,Jetty……
这些Servlet容器的代码虽然不同,但是他们都完整的实现了Servlet标准,也就是说,老师可以不管他们的代码,写的类可以在他们的容器中完美运行。
到这里第二个问题也解决了,Servlet的作用就是:
为java程序提供一个统一的web应用的规范。
当别人问起你知道Servlet么?
你可以自信滴讲,唉,知道,就是一个interface而已嘛~
二、ServletRequest ServletResponse
(一)ServletRequest
在Servlet中,ServletRequest是一个非常重要的接口。这个类是由Servlet容器进行实例化,然后作为参数传递给了service()方法。在HttpServlet中,对应的是HttpServletRequest接口,HttpServletRequest 接口继承与ServletRequest接口。
ServletRequest作用:
Servlet容器对于接收到的每一个请求,都会创建一个ServletRequest对象,并把这个对象作为参数传递给Servlet的sevice( )方法。ServletRequest对象封装了关于这次请求的相关信息。继承该接口的接口,可以携带一些和协议相关的特定信息,比如javax.servlet.http.HttpServletRequest接口,就携带了一些HTTP协议的一些信息。
1.ServletRequest方法
public Object getAttribute(String name);
获取ServletRequest对象中的对应name的参数值。在ServletRequest对象中,参数的设置有两种方式:
- 一种是由Servelt容器根据不同的请求而添加的参数信息,比如Https请求,Servlet容器就会根据客户端的证书,生成javax.servlet.request.X509Certificate对象,并添加到ServletRequest实例中;
- 还有一种是通过编程方式添加的参数,即通过ServletRequest.setAttribute()方法添加,这种方式必须在调用RequestDispatcher对象之前进行。
public Enumeration getAttributeNames();
返回ServletRequest对象中,所有属性的key对应的集合,返回类型Enumeration。
public void setAttribute(String name, Object o);
添加属性到request请求的方法,上面提到的编程式添加属性方法,一般与RequestDispatcher配合使用。如果添加的对象o=null的话,相当于执行了removeAttribute方法,即移除参数操作。还需要注意的是:name的命名方式,所有以java.*、javax.*、sun.*、com.sun.*、oracle.*、com.oracle.*字符串开头的都作为预留,不允许开发者使用。
public void removeAttribute(String name);
移除该请求中的一个参数。
public String getCharacterEncoding();
获取该request请求体的字符编码,如果没有设置则返回null。
public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException;
设置该request请求体的字符编码,会覆盖原有的字符编码。需要在获取参数前进行设置。
public int getContentLength(); 和 public long getContentLengthLong();
两个方法作用基本一样,连官方文档的解释基本上都是一模一样的。区别:getContentLengthLong()方法是从servlet3.1开始引入的;还有就是请求体长度的限制,getContentLengthLong()方法取消了Integer#MAX_VALUE的限制(未验证)。
public String getContentType();
返回请求体的MIME类型。
public ServletInputStream getInputStream() throws IOException;
获取请求体的二进制流 ServletInputStream 对象。读取请求体内容的方法还有getReader()方法,但是两者不能同时使用。
public BufferedReader getReader() throws IOException;
获取请求体的字符串数据,用BufferedReader对象表示,转换成字符串数据的时候,使用当前设置的字符串进行转码。和getInputStream()方法类似,同样是两者不能同时使用。
public String getRemoteAddr();
获取客户端的IP地址。
public String getRemoteHost();
获得客户端的主机名。在获取主机名的过程中,会涉及到dns查询,所以对性能上会有所影响。
public int getRemotePort();
获取客户端的端口号。
public String getLocalName();
获取服务器的主机名。
public String getLocalAddr();
获取服务器的IP地址。
public int getLocalPort();
获取服务器的端口号。
public Locale getLocale();
获取请求的Locale信息,该信息根据请求头中的Accept-Language来判断,如果没有该请求头,则使用默认的Locale对象。
public Enumeration<Locale> getLocales();
和getLocale()方法类似,只不过该方法返回的是集合,而且集合中的Locale对象的优先级依次递减。
(二)ServletResponse
三、HttpServletRequest
(一)HttpServletRequest介绍
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。
(二)HttpServletRequest常用方法
request.setAttribute和request.getAttribute()
request.getAttribute("nameOfObj"); 可得到jsp页面表单中输入框内的value。(其实表单控件中的Object的name与value是存放在一个哈希表中的,所以在这里给出Object的name会到哈希表中找出对应它的value)
request.setAttribute(position,nameOfObj);属于页面之间的传值,从a.jsp到b.jsp一次传递,之后这个request就会失去他的作用范围,再传就要重新设一个request.setAttribute()。(使用session.setAttribute()会在一个过程中始终保持这个值)
注:javascript与jsp中不能相互传值,因为javascript运行在客户端,jsp运行在服务器端。若想使他们能够相互传参数,可以在jsp页面中设置一个hidden属性的控件,用它的value来传递所需的数值。
getHeader
我们将使用 request.getHeader() 来获取请求的 header 。header 告诉 server 请求的详细信息。 例如,如果 header 是一个 cookie,则 server 将根据该数据工作。
语法:
request.getHeader(NAME);
NAME - 当我们设置 header 时,它有一个带有 value 的 name 。 我们需要通过 name 来获取 value 。
getParameter
tomcat或者其他的Servlet容器在调用程序员的Servlet之前已经帮我们做了很多事情了,比如解析HttpRequestLine(HTTP请求行)和解析HttpHeader(HTTP请求头)等等,但事实上在Servlet之前,服务器只解析到Header就停了,剩下的请求体留在request.getInputStream的流里,所以,如果你想在Servlet里面获得请求体里的内容,直接去读request.getInputStream()就好了。
但事情可能还没那么简单,我们知道,当我们在前端提交一个表单的时候,假设表单的内容为:name=baolin&password=mima
,我们是可以直接在Servlet里调用request.getParameter("name");
来获取到name的值的,大家有没有想过,如果form表单用的是POST方法提交,那么毫无疑问参数是在请求体里的,前面说过,如果想获取请求体里的内容,是可以在request流里直接读的,那么问题来了:
- 因为流是不能往回读的,如果我在之前先用
request.getInputStream()
读取完了请求体,这时候我再来用request.getParameter("name");
获取name的值,得到的是真实值还是个空呢?或者我先调用了request.getParameter();再来request.getInputStream();尝试获取请求体内容,可以得到吗? - 第二个问题,如果我在请求行里的URI部分这么写:
/testServlet?name=baolin
,接着又在请求体里附加name=baobao
,然后post提交,这时候在后台Servlet调用request.getParameter("name");
获取的是baolin还是baobao呢? - 第三个问题,如果我写了两个一样的参数,我应该怎样获取这两个参数呢?
答案:
- (该问题的前提是参数在请求体里,并且Content-Type是
application/x-www-form-urlencoded
)因为流是不能往回读的,所以如果程序员自己通过request获取的InputStream读完了请求体,那么再来调用request.getParameter试图获取参数得到的是null,反之亦然。 - request.getParameter()方法的解析顺序为:URI -> 请求体,所以如果在URI获取到了需要的参数,那就不会再去请求体解析了
【很明显这句是有问题的,Tomcat解析Parameter的逻辑应该是:parameter的name和value是一对多的关系,tomcat会先解析URI里的queryString,然后判断如果是post请求&&content-type="aplication/x-www-form-urlencoded"不管之前有没有获取到需要的参数都会去解析body,也就是说如果不是post请求而是delete或者其它请求或者content-type不是"aplication/x-www-form-urlencoded"则不会去读取body,这种情况getInputSteam读取body会得到数据】
- 可以用request.getParameterValues(“name”)方法来获取所有name
getRequestURI和getPathInfo
函数 getRequestURI() 返回完整的 requested URI 。 这包括 deployment folder 和 servlet-mapping string 。 它还将返回所有额外的路径信息。
函数 getPathInfo() 只返回传递给 servlet 的路径。 如果没有传递额外的路径信息,此函数将返回 null。
换句话说,如果我们将 application 部署在 Web 服务器的根目录中,并且请求映射到“/”的 servlet,则 getRequestURI() 和 getPathInfo() 将返回相同的字符串。 否则,我们会得到不同的值。
为了更好地理解 HttpServletRequest 方法,假设我们有一个可以通过此 URL 访问的 servlet:
http://localhost:8080/deploy-folder/servlet-mapping
该请求将命中部署在“deploy-folder”中的 Web 应用程序中的“servlet-mapping”servlet。 因此,如果我们为此请求调用 getRequestURI() 和 getPathInfo(),它们将返回不同的字符串。
让我们创建一个简单的 doGet() servlet 方法:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter writer = response.getWriter(); if ("getPathInfo".equals(request.getParameter("function")) { writer.println(request.getPathInfo()); } else if ("getRequestURI".equals(request.getParameter("function")) { writer.println(request.getRequestURI()); } writer.flush(); }
首先,让我们看一下通过 curl 命令获取的 getRequestURI 请求的 servlet 的输出:
curl http://localhost:8080/deploy-folder/servlet-mapping/request-path?function=getRequestURI /deploy-folder/servlet-mapping/request-path
同样,让我们看一下 getPathInfo 的 servlet 的输出:
curl http://localhost:8080/deploy-folder/servlet-mapping/request-path?function=getPathInfo /request-path
getservletcontext()
在 Java Servlet API 中,getServletContext()
方法是一个 ServletRequest 接口的方法,它可以用来获取与当前请求相关联的 ServletContext 对象,并且这个 ServletContext 对象代表了整个 Web 应用。
而 getSession().getServletContext()
则是通过该请求获取的 HttpSession 对象来获取 ServletContext 对象,它们的结果是相同的。
因此,在大多数情况下,使用 request.getServletContext()
和 request.getSession().getServletContext()
是等效的。
public class ServletContext01 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.获取对象 ServletContext context = getServletContext(); String address = context.getInitParameter("address"); System.out.println(address); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
web.xml参数设置:
<context-param> <param-name>address</param-name> <param-value>中国深圳</param-value> </context-param>
这个是一个比较经典的doGet和doPost方法的案例,继承了httpservlet类,
ServletContext context = getServletContext(); 其实我们可以这么理解,一个servlet可以使用getservletcontext()方法得到了web应用中的servletcontext,从而使用servletcontext接口的一些方法:比如我们可以看到后面的那句话String address = context.getInitParameter("address");实际上就是用了servletcontext接口里面的getInitParameter()方法:而address就是参数的名称,获得的值为中国深圳,所以有没有觉得这样很方便?
一个servlet可以通过ServletConfig对象的getServletContext()方法得到servlet上下文的引用,如果servlet直接或间接调用子类GenericServlet,则可以使用getServletContext()方法。
Web应用中servlet可以使用servlet上下文得到:
1.在调用期间保存和检索属性的功能,并与其他servlet共享这些属性。
2.读取Web应用中文件内容和其他静态资源的功能。
3.互相发送请求的方式。
4.记录错误和信息化消息的功能。
servletcontext接口里面的方法:
1.String getInitParameter(String name) 返回指定上下文范围的初始化参数值。
2.Object getAttribute(String name) 返回servlet上下文中具有指定名字的对象,或使用已指定名捆绑一个对象。从Web应用的标准观点看,这样的对象是全局对象,因为它们可以被同一servlet在另一时刻访问。或上下文中任意其他servlet访问。
3.void setAttribute(String name,Object obj) 设置servlet上下文中具有指定名字的对象。
4.Enumeration getAttributeNames() 返回保存在servlet上下文中所有属性名字的枚举。
5.ServletContext getContext(String uripath) 返回映射到另一URL的servlet上下文。在同一服务器中URL必须是以“/”开头的绝对路径。
6.Enumeration getInitParameterNames() 返回(可能为空)指定上下文范围的初始化参数值名字的枚举值。
7.int getMajorVersion() 返回此上下文中支持servlet API级别的最大和最小版本号。
8.int getMinorVersion()
9.String getMimeType(String fileName) 返回指定文件名的MIME类型。典型情况是基于文件扩展名,而不是文件本身的内容(它可以不必存在)。如果MIME类型未知,可以返回null。
10.RequestDispatcher getNameDispatcher(String name) 返回具有指定名字或路径的servlet或JSP的RequestDispatcher。如果不能创建RequestDispatch,返回null。如果指定路径,必须心“/”开头,并且是相对于servlet上下文的顶部。
11.RequestDispatcher getNameDispatcher(String path)
12.URL getResource(String path) 返回相对于servlet上下文或读取URL的输入流的指定绝对路径相对应的URL,如果资源不存在则返回null。
13.InputStream getResourceAsStream(String path)
14.String getServerInfo() 返顺servlet引擎的名称和版本号。
15.void log(String message)
16.void log(String message,Throwable t) 将一个消息写入servlet注册,如果给出Throwable参数,则包含栈轨迹。
17.void removeAttribute(String name) 从servlet上下文中删除指定属性。
request.getSession()方法
Session在网络应用中被称为会话。
具体到web中的Session指的就是用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间,因此从概述上我们可以看到,session实际上是一个特定的时间概念。
需要注意的是:一个session的概念需要包括特定的客户端,特定的服务器端以及不中断的操作时间。A用户和C服务器建立连接时所处的session同B用户和C服务器建立连接时所处的Session是两个不同的session。
Session的工作原理:
- 当一个session第一被启动时,一个唯一的标识被存储与本地的cookie中;
- 首先使用session_start()函数,PHP从session仓库中加载已经存储的session变量。
- 当执行PHP脚本时,通过session_register()函数注册session变量。
- 当PHP 脚本执行结束时,未被销毁的session变量会被自动保存在本地的一定路径下的session库中,这个路径可以通过php.ini文件中的session_save_path指定,下次浏览器网页时可以加载使用。
HttpRequest对象有两种形式的getSession的方法调用:
- getSession()
- getSession(boolen isNew)
这样,前者会检测当前时候是否有session存在,如果不存在则创建一个,如果存在就返回当前的。
getSession()相当于getSession(true);
参数为true时,若存在会话,则返回该会话,否则新建一个会话;
参数为false时,如存在会话,则返回该会话,否则返回NULL;
四、HttpServletResponse
(一)HttpServletResponse介绍
javax.servlet.http.HttpServletResponse继承了ServletResponse。专门为基于HTTP协议的HTTPServlet 封装、提供响应信息,是响应的抽象。
在客户端发出每个HTTP请求时,服务器都会创建一个对应的HttpServletResponse对象,然后在调用Servlet.service()方法时传递给service()方法,随后又会传递给对应类型的服务方法(doGet、doPost 等)中,开发中是可以直接通过该对象来设置响应的信息,tomcat服务器会从该对象中获取响应的信息,响应给客户端。
response对象的功能分为以下四种:
- 设置响应头信息;
- 设置响应状态码;
- 设置响应正文,发送响应。
- 重定向;
(二)HttpServletResponse功能
1.设置响应头信息
void setHeader(String name,value)
设置具有给定 name 和 value 的响应标头。使用该方法设置的响应头最终会发送给客户端浏览器!如果已设置某个响应头,则新值将覆盖旧值。注意addheader()方法将不会覆盖。
该方法模拟了html的meta标签,第一个参数相当于meta标签的http-equiv/name属性,第二个参数相当于meta标签的content属性。http-equiv顾名思义,相当于http的文件头作用,它可以向浏览器传回一些有用的信息,以帮助正确和精确地显示网页内容,与之对应的属性值为content,content中的内容其实就是各个参数的变量值。即key-value。
上面的方法可设置所有类型的响应头,当然也可以针对不同类型使用不同方法。如下:
void setDateHeader(String name,long date)
设置具有给定名称和日期值的响应头。日期是以时间周期以来的毫秒为单位指定的。如果已设置响应头, 则新值将覆盖上一个值。
void setIntHeader(String name,int value)
设置具有给定名称和整数值的响应头。如果已设置头, 则新值将覆盖上一个值。
2.设置状态码
void setStatus(int sc)
设置此响应的状态代码。上述方法用于在没有错误时设置返回状态代码。sc-状态码
void sendError(int sc)
上述方法使用指定的错误状态码向客户端发送错误响应, 并清除缓冲区。sc-错误状态码
void sendError(int sc, msg)
上述方法使用指定的错误状态码向客户端发送错误响应, 并清除缓冲区。传入的描述性信息,将覆盖默认的状态码的描述性信息。sc-错误状态码 msg-描述性信息
void setStatus(int sc,java.lang.String sm)
该方法已被废弃。由于从2.1 版开始, 由于消息参数的含义不明确,不适合自定义描述性信息。
3.设置响应正文
给客户端浏览器发送的内容,可以显示在浏览器窗体中。
4.发送重定向
当你访问www.sun.com时,你会发现浏览器地址栏中的URL会变成https://www.oracle…
重定向的特点:
- 重定向是两次请求两次响应。
- 地址栏发生变化。
- 重定向是客户端的行为。
- 重定向的URL不局限于当前应用的资源,可以指向其他应用的资源以及定位到站外资源。
- 重定向的响应码为302,并且必须要有Location响应头。
设置重定向:
响应码为200表示响应成功,而响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为302。
因为重定向是通知浏览器再发出第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置Location头,指定第二个请求的URL地址。
@WebServlet("/redirect-Servlet") public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //让redirect-Servlet的请求重定向到http://www.baidu.com resp.setStatus(302); resp.setHeader("Location", "http://www.baidu.com"); } }
上面代码的作用是:当访问RedirectServlet后,会通知浏览器重定向到百度主页。客户端浏览器解析到响应码为302后,就知道服务器让它重定向,所以它会马上获取响应头Location中的URL,然后发出第二个请求。
便捷的重定向方式:
response.sendRedirect()方法会设置响应码为302,并且设置Location响应头,是一个重定向的快捷方法。
设置了重定向之后,后面的的代码还会继续执行,并且执行完毕之后才会真正的响应给客户端,如果有Write输出响应数据,那么没有任何作用。使用sendRedirect方法后,会将响应视为已提交,如果响应已提交,则调用sendRedirect方法将引发IllegalStateException。我们不应该在提交响应之后调用其他提交响应或者不能在提交响应之后调用的方法,比如sendError、sendRedirect、setTrailerFields。
@WebServlet("/redirect-Servlet") public class RedirectServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //让redirect-Servlet的请求重定向到http://www.baidu.com //resp.setStatus(302); //resp.setHeader("Location", "http://www.baidu.com"); //便捷重定向 resp.sendRedirect("//www.baidu.com"); //响应提交之后不能调用其他提交响应的方法 //resp.sendRedirect("//www.baidu.com"); //之后的代码会执行,并且执行完毕之后才会响应给客户端 System.out.println(1111111); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3)); System.out.println(2222222); } }
上面的案例,在请求3秒之后才会重定向到百度的首页。
重定向的另一个方式就是通过response.setHeader("Refresh","5;URL=www.baidu.com")方法设置Refresh响应头,在指定时间(秒)后自动跳转到对应的页面。
重定向的URL路径:
重定向是服务器通知浏览器去访问另一个地址,即再发出另一个请求,因此重定向是客户端行为,使用的URL路径也是客户端路径。实际上超链接、表单提交、ajax、重定向的路径都是客户端路径。
客户端路径可以分为相对路径和绝对路径,相对路径又可以分为加不加“/”的情况.
- 绝对路径: 通常的格式为请求协议://域名(IP):端口(可无)/资源路径,可以不带请求协议,默认是http协议,但是在开头必须要带上“//”。比如“www.baidu.com”,或者“//www.baidu.…
- 以“/”开头的相对路径: 相对于当前虚拟主机的路径,URL格式为“/Web应用目录/资源路径”。比如当前主机路径为http://localhost:8080,URL为“/B/c.html”,那么实际上重定向的URL绝对路径地址为:http://localhost:8080/B/c.htm
- 不以“/”开头的相对路径: 当对于当前路径,即相对于当前资源的路径,URL格式为“资源路径”。比如当前资源路径为http://localhost:8080/A/abc,URL为“b.html”,那么实际上重定向的URL绝对路径地址为:http://localhost:8080/A/b.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了