皓学IT:WEB05-Servlet

一、Servlet

1.1.概述

Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。我们可以像学习Java基础一样,通过API来学习Servlet。这里需要注意的是,在我们之前JDK的API中是没有Servlet规范的相关内容,需要使用JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8,该网址中介绍了JavaEE8的一些新特性。当然,我们可以通过访问官方API,学习和查阅里面的内容。

打开官方API网址,在左上部分找到javax.servlet包,在左下部分找到Servlet,如下图显示:

翻译如下

通过阅读API,我们得到如下信息:

第一:Servlet是一个运行在web服务端的java小程序

第二:它可以用于接收和响应客户端的请求

第三:要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet

第四:每次请求都会执行service方法

第五:Servlet还支持配置

servlet就是一个java程序,用来处理请求和响应。

1.2. Servlet架构

下图展示了Servlet在Web应用程序中的位置:

1.3. Servlet任务

用来处理从客户端发过来的请求request,生成响应response

  1. 获取请求数据

  2. 处理请求

  3. 完成响应

1.4. Servlet相关知识

1.4.1. Servlet加载时机

在默认情况下,当Web客户第一次请求访问某个Servlet时,Web容器会创建这个Servlet的实例。

当设置了web.xml中的子元素后,Servlet容器在启动Web应用时,将按照指定顺序创建并初始化这个Servlet。设置的数值大于0即可。例如:

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.langsin.servlet.HelloServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

1.4.2. Servlet的生命周期

先看与Servlet生命周期有关的三个方法:init(), service(), destroy(). Servlet生命周期可被定义为从创建直到毁灭的整个过程。以下是三个方法分别对应的Servlet过程:

  • init():Servlet进行初始化;

  • service():Servlet处理客户端的请求;

  • destroy():Servlet结束,释放资源;

在调用destroy()方法后,Servlet由JVM的垃圾回首器进行垃圾回收。

现在我们来详细讨论Servlet生命周期的方法:

init()方法:

Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化在Servlet生命周期中init()方法只被调用一次。

当用户调用一个Servlet时,Servlet容器就会创建一个Servlet实例,每一个用户请求都会产生一个新的线程,init()方法简单的创建或加载一些数据,这些数据将会被用在Servlet的整个生命周期。

init()方法的定义如下:

public void init() throws ServletException{
    //初始化代码。。。
}

service()方法

service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service()方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。

每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service()方法检查HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用doGet()、doPost()等方法。

service()的定义如下:

public void service(ServletRequest request, ServletResponse response)
    throws ServletException, IOException{
    //service()代码。。。
}

destroy()方法

destroy()方法只会被调用一次,在Servlet生命周期结束时被调用。destroy()方法可以让Servlet关闭数据库连接、停止后台、把cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。在调用destroy()方法之后,Servlet对象被标记为垃圾回收。

destroy()方法的定义如下所示:

public void destroy(){
    //终止化代码。。。
}

总结:

  • 在首次访问某个Servlet时,init()方法会被执行,而且也会执行service()方法。

  • 再次访问时,只会执行service()方法,不再执行init()方法。

  • 在关闭Web容器时会调用destroy()方法。

1.5. 实现一个servlet

当服务器接收到一个请求,就要有一个servlet去处理这个请求,所以完成一个servlet通常需要两步走。一方面要写一个java程序定义一个servlet,另一方面要配置一下servlet确定这个servlet要处理哪一个请求。

1.5.1. 创建Servlet的三种方式

(1)实现javax.servlet.Servlet接口。

(2)继承javax.servlet.GenericServlet类

(3)继承javax.servlet.http.HttpServlet类

我们在日常开发中一般会使用第三种方法来进行Servlet的创建,前两种方法理解即可.。

注意:创建Servlet文件后,需要在web.xml文件中完成Servlet配置,才可以使用。

1.5.2. 配置Servlet的两种方式

  1. 使用web.xml文件配置Servlet。例如,我有一个名为UserServlet的Servlet,主要将它配置到服务器进行运行,可以按照下面的代码进行配置web.xml文件:

<servlet>
    <servlet-name>user</servlet-name>
    <servlet-class>com.xinzhi.controller.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>user</servlet-name>
    <url-pattern>/user.do</url-pattern>
</servlet-mapping>
  1. 使用注解进行Servlet配置(高版本后默认使用此方法):(学完位置再学注解)

    当我们去创建一个Servlet时会默认继承HttpServlet类,会使用注解方式进行配置Servlet:

//  这种方式配置的效果与第一种一致
@WebServlet(name = "/HelloServlet")

注意:两种配置方式不能同时使用,即配置了web.xml就不能使用注解,使用了注解也就不能使用web.xml配置了。

通过实现Servlet接口,这个接口定义了servlet的生命周期,所有的方法需要我们实现。

public class UserServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
​
    }
​
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
​
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().println("<h1>hello servlet</h1>");
    }
​
    @Override
    public String getServletInfo() {
        return null;
    }
​
    @Override
    public void destroy() {
​
    }
}

继承javax.servlet.http.HttpServlet类

public class MyServlet extends HttpServlet {
​
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
  • HttpServletRequest和ServletRequest都是接口

  • HttpServletRequest继承自ServletRequest

  • HttpServletRequest比ServletRequest多了一些针对于Http协议的方法。例如:getHeader(),getMethod() ,getSession()

1.6. Servlet的匹配规则

1.6.1. 四种匹配规则

(1) 精确匹配<url-pattern>中配置的项必须与url完全精确匹配。

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/users.html</url-pattern>
    <url-pattern>/index.html</url-pattern>
    <url-pattern>/user/addUser.action</url-pattern>
</servlet-mapping>

当在浏览器中输入如下几种url时,都会被匹配到该servlet

http://localhost:8080/appDemo/user/users.html

http://localhost:8080/appDemo/index.html

http://localhost:8080/appDemo/user/addUser.action

注意:

http://localhost:8080/appDemo/user/addUser.action/是非法的url,不会被当作http://localhost:8080/appDemo/user/addUser.action识别

另外上述url后面可以跟任意的查询条件,都会被匹配,如

http://localhost:8080/appDemo/user/addUser.action?username=Tom&age=23会被匹配到MyServlet。

url路径后跟?参数名=参数值&参数名=参数值(get请求方式)

(2) 路径匹配

以“/”字符开头,并以“/*”结尾的字符串用于路径匹配

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/*</url-pattern>
</servlet-mapping>

路径以/user/开始,后面的路径可以任意。比如下面的url都会被匹配。

http://localhost:8080/appDemo/user/users.html

http://localhost:8080/appDemo/user/addUser.action

http://localhost:8080/appDemo/user/updateUser.action

(3) 扩展名匹配**

以“*.”开头的字符串被用于扩展名匹配

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

则任何扩展名为jsp或action的url请求都会匹配,比如下面的url都会被匹配

http://localhost:8080/appDemo/user/users.jsp

http://localhost:8080/appDemo/toHome.do

(4) 缺省匹配

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

*默认default配置(防止DispatcherServlet拦截设置路径后缀)

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<!--使用Tomcat默认Servlet处理静态资源,该配置仅适用Tomcat容器-->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.gif</url-pattern>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

1.6.2. 匹配顺序

  1. 精确匹配。

  2. 路径匹配,先最长路径匹配,再最短路径匹配。

  3. 扩展名匹配。

注意:使用扩展名匹配,前面就不能有任何的路径。

4.缺省匹配,以上都找不到servlet,就用默认的servlet,配置为<url-pattern>/</url-pattern>

1.6.3. 需要注意的问题

路径匹配和扩展名匹配无法同时设置

匹配方法只有三种,要么是路径匹配(以"/"字符开头,并以“/*”结尾),要么是扩展名匹配(以“ *.”开头),要么是精确匹配,三种匹配方法不能进行组合,不要想当然使用通配符或正则规则。

  • 如<url-pattern>/user/*.action</url-pattern>是非法的

  • 另外注意:<url-pattern>/aa/*/bb</url-pattern>是精确匹配,合法,这里的 *不是通配的含义

“/*”和“/”含义并不相同

  • "/*"属于路径匹配,并且可以匹配所有request,由于路径匹配的优先级仅次于精确匹配,所以"/ *"会覆盖所有的扩展名匹配,很多404错误均由此引起,所以这是一种特别恶劣的匹配模式。

  • "/"是servlet中特殊的匹配模式,该模式有且仅有一个实例,优先级最低,不会覆盖其他任何url-pattern,只是会替换servlet容器的内建default servlet,该模式同样会匹配所有request。

Tomcat在%CATALINA_HOME%\conf\web.xml文件中配置了默认的Servlet,配置代码如下:

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

 

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

1.6.4. 举例

实际请求映射的结果

1.6.5.@WebServlet注解

属性名类型描述
nameString指定Servlet的name属性,等价于<servlet-name> 如果没有显式指定,则该Servlet的取值为类的全限定名
valueString[]该属性等价于urlPatterns属性。两个属性不能同时使用
urlPatternsString[]指定一组Servlet的URL匹配模式。等价于<url-pattern>标签
loadOnStartupint指定Servlet的加载顺序,其值为(0,1,2.....),值越小,优先级越高,等价于<load-on-startup>标签
initParamsWebInitParam[]指定一组Servlet初始化参数,等价于<init-param>标签 例:initParams = { @WebInitParam(name="name",value="IT老王"), @WebInitParam(name="age",value="18")}
asyncSupportedboolean声明Servlet是否支持异步操作模式,等价于<async-supported>标签
descriptionString该Servlet的描述信息,等价于<description>标签
displayNameString该Servlet的显示名,通常配合工具使用,等价于<display-name>标签

1.6.6.等效配置(举例)

package servlet;
​
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
​
@WebServlet(name="zhujie",
        urlPatterns = "/zhujie",
        loadOnStartup = 1,
        initParams = {
        @WebInitParam(name="name",value="IT老王"),@WebInitParam(name="age",value = "18")
        }
)
public class DemoServlet extends HttpServlet{
​
    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过 getServletName() 可以获取注解为该servlet指定的servlet名字(String)
        System.out.println("ServletName=" + getServletName());
        //通过 getInitParameter("name") 可以获取该servlet中参数名为name的值(String)
        System.out.println("name="+getInitParameter("name"));
        //通过 getInitParameter("age") 可以获取该servlet中参数名为age的值(String)
        System.out.println("age="+getInitParameter("age"));
    }
}
package servlet;
​
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
​
//在web.xml中,书写了与下面注解等效的配置
​
        /*@WebServlet(name="zhujie",
        urlPatterns = "/zhujie",
        loadOnStartup = 1,
        initParams = {
        @WebInitParam(name="name",value="IT老王"),
        @WebInitParam(name="age",value = "18")
        }
        )*/
//web.xml中的等效配置
        /*<servlet>
        <servlet-name>zhujie</servlet-name>
        <servlet-class>servlet.DemoServlet</servlet-class>
        <init-param>
        <param-name>name</param-name>
        <param-value>IT老王</param-value>
        </init-param>
        <init-param>
        <param-name>age</param-name>
        <param-value>18</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
        <servlet-name>zhujie</servlet-name>
        <url-pattern>/zhujie</url-pattern>
        </servlet-mapping>*/
​
public class DemoServlet extends HttpServlet{
​
    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
​
        //在控制台输出信息
        //通过 getServletName() 可以获取注解为该servlet指定的servlet名字(String)
        System.out.println("ServletName=" + getServletName());
        //通过 getInitParameter("name") 可以获取该servlet中参数名为name的值(String)
        System.out.println("name="+getInitParameter("name"));
        //通过 getInitParameter("age") 可以获取该servlet中参数名为age的值(String)
        System.out.println("age="+getInitParameter("age"));
​
        //在客户端显示信息
        resp.setContentType("text/html;charset=utf-8");
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        out.println("ServletName="+getServletName()+"<br>");
        out.println("name="+getInitParameter("name")+"<br>");
        out.println("age="+getInitParameter("age")+"<br>");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
           version="3.0">
    
    <!--创建名字为zhujie的servlet-->
    <servlet>
        <servlet-name>zhujie</servlet-name>
        <servlet-class>servlet.DemoServlet</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>IT老王</param-value>
        </init-param>
        <init-param>
            <param-name>age</param-name>
            <param-value>18</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>zhujie</servlet-name>
        <url-pattern>/zhujie</url-pattern>
    </servlet-mapping>
​
</web-app>

1.7. 请求和响应

1.7.1. 请求-request

1.7.1.1. request概述

request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。

request的功能可以分为以下几种:

  • 封装了请求头数据;

  • 封装了请求正文数据,如果是GET请求,那么就没有正文;

  • request是一个域对象,可以把它当成Map来添加获取数据;

  • request提供了请求转发和请求包含功能。(以后学习)

1.7.1.2. request获取请求头数据

request与请求头相关的方法有:

  • String getHeader(String name):获取指定名称的请求头;

  • Enumeration getHeaderNames():获取所有请求头名称;

  • int getIntHeader(String name):获取值为int类型的请求头。

1.7.1.3. request获取请求相关的其它方法

request中还提供了与请求相关的其他方法,有些方法是为了我们更加便捷的方法请求头数据而设计,有些是与请求URL相关的方法。

  • int getContentLength():获取请求体的字节数,GET请求没有请求体,没有请求体返回-1;

  • String getContentType():获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码;

  • String getMethod():返回请求方法,例如:GET

  • Locale getLocale():返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用;

  • String getCharacterEncoding():获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用ISO-8859-1编码;

  • void setCharacterEncoding(String code):设置请求编码,只对请求体有效!注意,对于GET而言,没有请求体!!!所以此方法只能对POST请求中的参数有效!

  • String getContextPath():返回上下文路径,例如:/hello

  • String getQueryString():返回请求URL中的参数,例如:name=zhangSan

  • String getRequestURI():返回请求URI路径,例如:/hello/oneServlet

  • StringBuffer getRequestURL():返回请求URL路径,例如:http://localhost/hello/oneServlet,

    即返回除了参数以外的路径信息;

  • String getServletPath():返回Servlet路径,例如:/oneServlet

  • String getRemoteAddr():返回当前客户端的IP地址;

  • String getRemoteHost():返回当前客户端的主机名,但这个方法的实现还是获取IP地址;

  • String getScheme():返回请求协议,例如:http;

  • String getServerName():返回主机名,例如:localhost

  • int getServerPort():返回服务器端口号,例如:8080

案例:request.getRemoteAddr():封IP

可以使用request.getRemoteAddr()方法获取客户端的IP地址,然后判断IP是否为禁用IP。

String ip = requset.getRemoteAddr();
if(ip.equals("127.0.0.1")){
    response.getWriter().println("您的IP已被禁止!");
}else{
    response.getWriter().println("hello!");
}
1.7.1.4. request获取请求参数

最为常见的客户端传递参数方式有两种:

  • 浏览器地址栏直接输入:一定是GET请求;

  • 超链接:一定是GET请求;

  • 表单:可以是GET,也可以是POST,这取决与<form>的method属性值;

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    super.doGet(request, response);
    String v1 = request.getParameter("p1");
    String v2 = request.getParameter("p2");
    System.out.println("p1=" + v1);
    System.out.println("p2=" + v2);
}
​
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    super.doPost(request, response);
    String v1 = request.getParameter("p1");
    String v2 = request.getParameter("p2");
    System.out.println("p1=" + v1);
    System.out.println("p2=" + v2);
}
  • String[] getParameterValues(String name):当多个参数名称相同时,可以使用方法来获取;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    super.doGet(request, response);
    String[] names = request.getParameterValues("name");
    System.out.println(Arrays.toString(names));
}
  • Enumeration getParameterNames():获取所有参数的名字;

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    super.doPost(request, response);
    Enumeration names = request.getParameterNames();
    while (names.hasMoreElements()){
        System.out.println(names.nextElement());
    }
}
  • Map getParameterMap():获取所有参数封装到Map中,其中key为参数名,value为参数值,因为一个参数名称可能有多个值,所以参数值是String[],而不是String。

Map<String, String[]> parameterMap = request.getParameterMap();
for (String name : parameterMap.keySet()){
    String[] values = parameterMap.get(name);
    System.out.println(name + ":" + Arrays.toString(values));
}
1.7.1.5. 请求转发

请求转发表示由 多个Servlet共同来处理一个请求 。例如Servlet1来处理请求,然后Servlet1又转发给Servlet2来继续处理这个请求。

在AServlet中,把请求转发到BServlet:

public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doGet(request, response);
        System.out.println("AServlet");
        RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
        rd.forward(request,response);
        return;
    }
}
public class BServlet extends HttpServlet {
​
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
        System.out.println("BServlet");
    }
}
1.7.1.6. request域方法

一个请求会创建一个request对象,如果在一个请求中经历了多个Servlet,那么多个Servlet就可以使用request来共享数据。

下面是request的域方法:

  • void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性。

  • Object getAttribute(String name):用来获取request中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)request.getAttribute(“xxx”);,获取名为xxx的域属性;

  • void removeAttribute(String name):用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;

  • Enumeration getAttributeNames():获取所有域属性的名称;

域方法通常在进行重定向时使用,多个servlet共享数据。

1.7.2. 响应-response

1.7.2.1. response概述

response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse。

在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。

response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。

response对象的功能分为以下四种:

  • 设置响应头信息;

  • 发送状态码;

  • 设置响应正文;

  • 重定向;

1.7.2.2. response响应正文

response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:

PrintWriter out = response.getWriter():获取字符流,处理字符;

ServletOutputStream out = response.getOutputStream():获取字节流,处理文件;

注意,在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。

字符响应流

(1)字符编码

重要:在使用response.getWriter()时需要注意默认字符编码为ISO-8859-1,如果希望设置字符流的字符编码为utf-8,可以使用response.setCharaceterEncoding(“utf-8”)来设置。这样可以保证输出给客户端的字符都是使用UTF-8编码的!

但客户端浏览器并不知道响应数据是什么编码的!如果希望通知客户端使用UTF-8来解读响应数据,那么还是使用response.setContentType("text/html;charset=utf-8")方法比较好,因为这个方法不只会调用response.setCharaceterEncoding(“utf-8”),还会设置content-type响应头,客户端浏览器会使用content-type头来解读响应数据。

(2)缓冲区

response.getWriter()是PrintWriter类型,所以它有缓冲区,缓冲区的默认大小为8KB。也就是说,在响应数据没有输出8KB之前,数据都是存放在缓冲区中,而不会立刻发送到客户端。当Servlet执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。

如果希望响应数据马上发送给客户端:

Ø 向流中写入大于8KB的数据;

Ø 调用response.flushBuffer()方法来手动刷新缓冲区;

1.7.2.3. 设置响应头信息

可以使用response对象的setHeader()方法来设置响应头!使用该方法设置的响应头最终会发送给客户端浏览器!

  • response.setHeader(“content-type”, “text/html;charset=utf-8”):

    设置content-type响应头,该头的作用是告诉浏览器响应内容为html类型,编码为utf-8。而且同时会设置response的字符流编码为utf-8,即response.setCharaceterEncoding(“utf-8”);

  • response.setHeader("Refresh","5; URL=http://www.baidu.cn"):5秒后自动跳转到百度主页。

1.7.2.4. 设置状态码及其他方法
  • response.setContentType("text/html;charset=utf-8"):等同与调用response.setHeader(“content-type”, “text/html;charset=utf-8”);用它就行了。

  • response.setCharacterEncoding(“utf-8”):设置字符响应流的字符编码为utf-8;

  • response.setStatus(200):设置状态码;

  • response.sendError(404, “您要查找的资源不存在”):当发送错误状态码时,Tomcat会跳转到固定的错误页面去,但可以显示错误信息。

1.7.2.5. 重定向(重要)

什么是重定向

当你访问http://www.sun.com时,你会发现浏览器地址栏中的URL会变成Hardware | Oracle,这就是重定向了。

重定向是服务器通知浏览器去访问另一个地址,即再发出另一个请求。

完成重定向

响应码为200表示响应成功,而响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为302。

因为重定向是通知浏览器再第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置Location头,指定第二个请求的URL地址。

public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doGet(request, response);
        response.setStatus(302);
    response.setHeader("Location","http://www.baidu.com");
    }
}

上面代码的作用是:当访问AServlet后,会通知浏览器重定向到百度主页。客户端浏览器解析到响应码为302后,就知道服务器让它重定向,所以它会马上获取响应头Location,然发出第二个请求。

便捷的重定向方法

public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect("http://www.baidu.com");
    }
}

response.sendRedirect()方法会设置响应头为302,以设置Location响应头。

如果要重定向的URL是在同一个服务器内,那么可以使用相对路径,例如:

public class AServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect("/servlet/BServlet");
    }
}

重定向的URL地址为:http://localhost:8080/servlet/BServlet

重定向小结

  • 重定向是两次请求,请求转发是一次

  • 重定向的URL可以是其他应用,不局限于当前应用;

  • 重定向的响应头为302,并且必须要有Location响应头;

  • 重定向就不要再使用response.getWriter()或response.getOutputStream()输出数据,不然可能会出现异常;

1.7.3. 重定向和转发的区别

  • 重定向是两次请求,转发是一个请求

  • 重定向是浏览器的行为,请求转发是服务器行为

  • 重定向浏览器的地址会发生改变,转发不会

  • 重定向可以重定向到任何地址,转发只能在项目内转发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/409583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

k8s笔记26--快速实现prometheus监控harbor

k8s笔记26--快速实现prometheus监控harbor 简介采集指标&配置grafana面板采集指标配置grafana面板 说明 简介 harbor是当前最流行的开源容器镜像仓库项目&#xff0c;被大量IT团队广泛应用于生产、测试环境的项目中。本文基于Harbor、Prometheus、Grafana介绍快速实现监控…

H5多用途的产品介绍展示单页HTML5静态网页模板

H5多用途的产品介绍展示单页HTML5静态网页模板 源码介绍&#xff1a;一款H5自适应多用途的产品介绍展示单页HTML静态网页模板&#xff0c;可用于团队官网、产品官网。 下载地址&#xff1a; https://www.changyouzuhao.cn/13534.html

MYSQL安装及卸载

目录 一、下载 二、解压 三、配置 1. 添加环境变量 2. 初始化MySQL 3. 注册MySQL服务 4. 启动MySQL服务 5. 修改默认账户密码 四、登录MySQL 五、卸载MySQL 一、下载 点开下面的链接&#xff1a;MySQL :: Download MySQL Community Server 点击Download 就可以下载对…

vim恢复.swp [BJDCTF2020]Cookie is so stable1

打开题目 扫描目录得到 关于 .swp 文件 .swp 文件一般是 vim 编辑器在编辑文件时产生的&#xff0c;当用 vim 编辑器编辑文件时就会产生&#xff0c;正常退出时 .swp 文件被删除&#xff0c;但是如果直接叉掉&#xff08;非正常退出&#xff09;&#xff0c;那么 .swp 文件就会…

爬虫入门五(Scrapy架构流程介绍、Scrapy目录结构、Scrapy爬取和解析、Settings相关配置、持久化方案)

文章目录 一、Scrapy架构流程介绍二、Scrapy目录结构三、Scrapy爬取和解析Scrapy的一些命令css解析xpath解析 四、Settings相关配置提高爬取效率基础配置增加爬虫的爬取效率 五、持久化方案 一、Scrapy架构流程介绍 Scrapy一个开源和协作的框架&#xff0c;其最初是为了页面抓取…

基于springboot+vue的音乐网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【其他】简易代码项目记录

1. KeypointDetection 1.1. CharPointDetection 识别字符中的俩个关键点。 1.2. Facial-keypoints-detection 用于检测人脸的68个关键点示例。 1.3. Hourglass-facekeypoints 使用基于论文Hourglass 的模型实现人体关键点检测。 1.4. Realtime-Action-Recognition containing:…

15.4K Star,超强在线编辑器

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 今天推荐一款非常棒的开源实时协作编辑器&#xff0c;可用于多人同时编…

复旦大学EMBA联合澎湃科技:共议科技迭代 创新破局

1月18日&#xff0c;由复旦大学管理学院、澎湃新闻、厦门市科学技术局联合主办&#xff0c;复旦大学EMBA项目、澎湃科技承办的“君子知道”复旦大学EMBA前沿论坛在厦门成功举办。此次论坛主题为“科技迭代 创新破局”&#xff0c;上海、厦门两地的政策研究专家、科学家、科创企…

JavaScript流程控制

文章目录 1. 顺序结构2. 分支结构2.1 if 语句2.2 if else 双分支语句2.3 if else if 多分支语句三元表达式 2.4 switch 语句switch 语句和 if else if语句区别 3. 循环结构3.1 for 循环断点调试 3.2 双重 for 循环3.3 while 循环3.4 do while 循环3.5 contiue break 关键字 4. …

3.WEB渗透测试-前置基础知识-快速搭建渗透环境(上)

上一个内容&#xff1a;2.WEB渗透测试-前置基础知识-web基础知识和操作系统-CSDN博客 1.安装虚拟机系统 linux Kali官网下载地址&#xff1a; https://www.kali.org/get-kali/#kali-bare-metal Centos官网下载地址&#xff1a; https://www.centos.org/download/ Deepin官网下…

Flask基础学习4

19-【实战】问答平台项目结构搭建_剪_哔哩哔哩_bilibili 参考如上大佬的视频教程&#xff0c;本博客仅当学习笔记&#xff0c;侵权请联系删除 问答发布的web前端页面实现 register.html {% extends base.html %}{% block head %}<link rel"stylesheet" href&q…

软件应用场景,物流货运配货单打印模板软件单据打印查询管理系统软件教程

软件应用场景&#xff0c;物流货运配货单打印模板软件单据打印查询管理系统软件教程 一、前言 以下软件以 佳易王物流快运单打印查询管理系统软件V17.1 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、打印模式可以分为 直打模式和套打模式 直打模…

在IDEA中创建vue hello-world项目

工作中最近在接触vue前端项目&#xff0c;记录一下从0搭建一个vue hello world项目的步骤 1、本地电脑安装配置node、npm D:\Project\vue\hello-world>node -v v14.21.3 D:\Project\vue\hello-world>npm -v 6.14.18 D:\Project\vue\hello-world> 2、设置npm国内淘…

2024最新前端面试题

一、css 浏览器渲染机制 定位margin 定位transform flex布局 二、js 数组是属于Object类型的&#xff0c;也就是引用类型&#xff0c;所以不能使用 typeof 来判断其具体类型。下面这些方法是判断数组的几种方法&#xff1a; 1、instanceof运算符 主要是判断某个实例&#xff08…

Movelt使用笔记-Movelt Setup Assistant

目录 Setup Assistant配置1 Start 加载urdf模型3 Virtual joints 虚拟关节5 Robot Poses 机器人位姿7 Passive Joints 被动关节8 Controllers 控制器9 Simulation 仿真10 3D Perception 3D感知11 Author Information 作者信息12 Configuration Files 配置文件启动MoveIt!Setup…

贪心算法学习

贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法。贪心算法在有最优子结构的问题中尤为有效。然而&#xff0c;要注意的是贪心…

推荐莹莹API管理系统PHP源码

莹莹API管理系统PHP源码附带两套模板,PHP版本要求为5.6至8.0之间&#xff0c;已测试通过的版本为7.4。 需要安装PHPSG11加密扩展。 已测试&#xff1a;宝塔/主机亲测成功搭建&#xff01; 演示地 址 &#xff1a; runruncode.com/php/19698.html 安装说明&#xff08;适用于宝…

全网唯一基于共享内存的C++ RPC框架

首先声明&#xff1a;我不是标题党&#xff0c;我是在找遍全网&#xff0c;没有找到一个基于共享内存实现、开源且跨平台的C RPC框架之后&#xff0c;才着手开发的这个框架。 项目地址&#xff1a;https://github.com/winsoft666/veigar 1. Veigar Veigar一词来源于英雄联盟里…

数据库应用:Windows 部署 MySQL 8.0.36

目录 一、实验 1.环境 2.Windows 部署 MySQL 8.0.36 3.Windows配置环境变量 4.Navicat链接MySQL 二、问题 1.安装MySQL 报错 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机软件版本IP备注WindowsMySQL8.0.36localhost 2.Windows 部署 MySQL 8.0.…