JavaWeb - 10 请求与响应相关操作

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 1.请求对象(ServletRequest)
    • 1.1 请求对象相关概述
    • 1.2 常用的请求对象的继承关系
    • 1.3 常用的方法
    • 1.4 请求参数的封装
    • 1.5 用流的形式读取信息
    • 1.6 解决中文乱码问题
    • 1.7 请求转发
    • 1.8 ServletRequest域对象
    • 1.9 请求包含
  • 2.响应对象(ServletResponse)
    • 2.1 响应对象的概述
    • 2.2 响应对象类的继承结构图
    • 2.3 响应对象的方法
    • 2.4 响应输出中文乱码问题
    • 2.5 设置响应头-定时刷新/跳转
    • 2.6 请求重定向
    • 2.7 消息请求头响应对象-文件下载
  • 3.其它问题及注意细节
    • 3.1 请求重定向与请求转发的区别
    • 3.2 注意的细节问题


1.请求对象(ServletRequest)

1.1 请求对象相关概述

  • 我们先去api文档看看官方文档

    	Defines an object to provide client request information to a servlet. The servlet container creates a ServletRequest object and passes it as an argument to the servlet's service method. 
    	定义一个对象,向servlet提供客户端请求信息。servlet容器创建一个ServletRequest对象,并将其作为参数传递给servlet的服务方法。
        A ServletRequest object provides data including parameter name and values, attributes, and an input stream. Interfaces that extend ServletRequest can provide additional protocol-specific data (for example, HTTP data is provided by HttpServletRequest.)
        ServletRequest对象提供数据,包括参数名称和值、属性和输入流。扩展ServletRequest的接口可以提供附加的特定于协议的数据(例如,HTTP数据是由HttpServletRequest提供的。)
    
  • ServletRequest对象提供了客户端的请求信息给Servlet对象。由服务器通过Servlet中service方法参数将信息传给Servlet。

  • ServletRequest包含了客户端发来的数据,如:参数名称、参数值、属性及输入流。

  • HttpServletRequest是专门针对Http协议的请求对象。

  • 请求,顾明思议,就是使用者希望从服务器端索取一些资源,向服务器发出询问。在B/S架构中,就是 客户浏览器向服务器发出询问。在我们的JavaEE工程中,客户浏览器发出询问,要遵循HTTP协议所规 定的。

  • 请求对象,就是在JavaEE工程中,用于发送请求的对象。我们常用的对象就是ServletRequest和 HttpServletRequest,它们的区别就是是否和HTTP协议有关。

1.2 常用的请求对象的继承关系

JavaWeb - 10 请求与响应相关操作_第1张图片

1.3 常用的方法

JavaWeb - 10 请求与响应相关操作_第2张图片

代码演示-请求行信息的获取

/**
 * 请求行相关方法:
 * getMethod 获取请求方式
 * getContextPath 获取项目访问路径,等价于 servletContext.getContextPath
 * getServletPath 获取Servlet访问路径
 * getRemoteAddr 获取客户端的IP地址
 * getLocalAddr 获取服务器的IP地址
 * getLocalName 获取服务器名称
 * getLocalProt 获取服务器端口
 * getRequestURL 获取统一资源定位符(带协议的绝对路径)
 * getRequestURI 获取同意资源标识符(不带协议的绝对路径)
 * getQueryString 获取URL后面拼接的参数字符串
 */
@WebServlet("/demo01")
public class ServletDemo01 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // * getMethod 获取请求方式
        String method = request.getMethod();
        System.out.println("method = " + method);

        // * getContextPath 获取项目访问路径,等价于 servletContext.getContextPath
        String contextPath = request.getContextPath();
        System.out.println("contextPath = " + contextPath);

        // * getServletPath 获取Servlet访问路径
        String servletPath = request.getServletPath();
        System.out.println("servletPath = " + servletPath);

        // * getRemoteAddr 获取客户端的IP地址
        String remoteAddr = request.getRemoteAddr();
        System.out.println("remoteAddr = " + remoteAddr);

        // * getLocalAddr 获取服务器的IP地址
        String localAddr = request.getLocalAddr();
        System.out.println("localAddr = " + localAddr);

        // * getLocalName 获取服务器名称
        String localName = request.getLocalName();
        System.out.println("localName = " + localName);

        // * getLocalProt 获取服务器端口
        int localPort = request.getLocalPort();
        System.out.println("localPort = " + localPort);

        // * getRequestURL 获取统一资源定位符(带协议的绝对路径)
        StringBuffer requestURL = request.getRequestURL();
        System.out.println("requestURL = " + requestURL);

        // * getRequestURI 获取同意资源标识符(不带协议的绝对路径)
        String requestURI = request.getRequestURI();
        System.out.println("requestURI = " + requestURI);

        // * getQueryString 获取URL后面拼接的参数字符串
        String queryString = request.getQueryString();
        System.out.println("queryString = " + queryString);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

代码演示-请求头信息的获取

/**
 * 请求头信息获取相关方法:
 * getHeader 获取请求头的一个值
 * getHeaders 获取请求头的多个值
 * getHeaderNames 获取所有请求头的名称
 */
@WebServlet("/demo02")
public class ServletDemo02 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // getHeader 获取请求头的一个值
        String header = request.getHeader("Accept-Language");
        System.out.println("header = " + header);
        System.out.println("----------------------");

        // getHeaders 获取请求头的多个值
        Enumeration<String> headers = request.getHeaders("Accept-Encoding");
        while (headers.hasMoreElements()) {
            String element = headers.nextElement();
            System.out.println("element = " + element);
        }
        System.out.println("----------------------");

        // getHeaderNames 获取所有请求头的名称
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            System.out.println("name = " + name);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

代码演示-获取请求参数

准备一个表单提交的页面

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>获取请求参数title>
head>
<body>
<form action="/day12/demo03" method="get" enctype="application/x-www-form-urlencoded">
    姓名:<input type="text" name="name"><br>
    年龄:<input type="text" name="age"><br>
    爱好:<input type="checkbox" name="hobbys" value="足球">足球
    <input type="checkbox" name="hobbys" value="羽毛球">羽毛球
    <input type="checkbox" name="hobbys" value="其它">其它<br>
    <button type="submit">提交button>
form>
body>
html>

Java代码

/**
 * 请求头信息获取相关方法:
 * getParameter 根据参数名称获取一个值
 * getParameterValues 根据参数名称获取多个值
 * getParameterNames 获取所有的参数名称
 * getParameterMap 键是参数名称,值是多个参数值
 */
@WebServlet("/demo03")
public class ServletDemo03 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // * getParameter 根据参数名称获取一个值
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("-----------------------------");

        // * getParameterValues 根据参数名称获取多个值
        String[] hobbys = request.getParameterValues("hobbys");
        System.out.println("hobbys = " + Arrays.toString(hobbys));
        System.out.println("-----------------------------");

        // * getParameterNames 获取所有的参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parametername = parameterNames.nextElement();
            System.out.println("parametername = " + parametername);
        }
        System.out.println("-----------------------------");

        // * getParameterMap 键是参数名称,值是多个参数值
        Map<String, String[]> parameterMap = request.getParameterMap();
        System.out.println("parameterMap = " + parameterMap);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

1.4 请求参数的封装

我们通过上面的方法可以获取到请求参数,但是如果参数过多,在进行传递时,方法的形参定义将会变 得非常难看。此时我们应该用一个对象来描述这些参数,它就是实体类。这种类的定义,从基础阶段我们就开始使用了。就是将表单或是其他形式传过来的数据进行封装,包装成一个描述类。就比如上述表单,将其数据封装成一个Person描述类。我们现在要做的就是把表单中提交过来的数据填充到实体类中。

对于上述表单我们可以将Person描述类定义成如下:

public class Person {
    private String Name;
    private Integer age;
    private String[] hobbys;
    // 构造参数、getter、setter等方法自行生成
}

封装方式一:最简单的直接封装

/**
  * 封装方式一:直接封装
  * @param request
  * @param response
  */
private void packageOne(HttpServletRequest request, HttpServletResponse response) {
    String name = request.getParameter("name");
    String age = request.getParameter("age");
    String[] hobbys = request.getParameterValues("hobbys");
    Person person = new Person(name, Integer.parseInt(age), hobbys);
    System.out.println("person = " + person);
}

这种方式的缺点太明显,就是无论有几个参数,我们还是要手动的一个一个的向事务描述类中添加属性元素,太麻烦了。

封装方式二:通过getParameterMap方法配合反射技术进行封装

首先我们要对封装类进行改造

// 改造的主要是age属性的构造方法和getter与setter方法
public Person(String name, String age, String[] hobbys) {
    Name = name;
    this.age = Integer.parseInt(age);
    this.hobbys = hobbys;
}
public String getAge() {
    return age+"";
}
public void setAge(String age) {
    this.age = Integer.parseInt(age);
}

Java代码

/**
 * 封装方式二:通过getParameterMap方法配合反射技术进行封装
 * @param request
 * @param response
 */
private void packageTwo(HttpServletRequest request, HttpServletResponse response) {
    // 1.创建描述类对象
    Person person = new Person();

    // 2.获取请求正文的映射关系
    Map<String, String[]> map = request.getParameterMap();

    // 3.遍历map集合
    Set<Map.Entry<String, String[]>> entries = map.entrySet();

    for (Map.Entry<String, String[]> entry : entries) {
        // 获取参数名称
        String name = entry.getKey();
        // 获取值
        String[] value = entry.getValue();

        try {
            // 拿到User对象中的属性描述器。是谁的属性描述器:是由构造函数的第一个参数决定的。
            // 第二个参数是指定javabean的字节码
            // 参数指的就是拿哪个类的哪个属性的描述器
            PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, Person.class);
            // 设置javabean属性的值
            Method method = propertyDescriptor.getWriteMethod();

            // 判断参数值是否有多个
            if (value.length > 1){
                method.invoke(person,(Object) value); // 第一个参数是指的给哪个对象,
                // 第二个参数指的是赋什么值
            }else {
                method.invoke(person,value); // 第一个参数是指的给哪个对象,
                // 第二个参数指的是赋什么值
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    System.out.println("person = " + person);
}

当我们写完此种封装方式之后,同学们可以发现,我们绝大多数封装都可以使用这段代码来实现。并 且,无论是谁来写这段通用的封装代码,其代码内容都是大同小异的。那么,我们就可以得出一个很有 趣的结论:一般遇到这种情况时,肯定有人帮我们写好了,我们只需要用就行了。我们后面还会遇到类 似这样的情况。 此时,帮我们写好这段封装代码的是apache软件基金会,我们前面学习的tomcat也是它提供的。它里 面有一个开源工具包集合commons,里面有很多开源工具类,今天我们就来讲解第一个:commons-beanutils

方式三:commons-beanutils方式创建

  1. 导包人commons-beanutils包
  2. 编写代码
/**
 * 方式三:通过BeanUtils工具类创建
 * @param request
 * @param response
 */
private void packageThree(HttpServletRequest request, HttpServletResponse response)  {
    Person person = new Person();

    try {
        BeanUtils.populate(person,request.getParameterMap());
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    System.out.println("person = " + person);
}

1.5 用流的形式读取信息

方法:getInpuStream();

/**
 * 通过流的方式读取信息:
 */
@WebServlet("/demo05")
public class ServletDemo05 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取流对象
        ServletInputStream inputStream = request.getInputStream();

        // 读取数据
        int len = 0;
        byte[] bys = new byte[1024];
        while ((len = inputStream.read(bys)) != 0) {
            System.out.println(new String(bys, 0, len));
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

1.6 解决中文乱码问题

关于请求中文乱码问题,我们需要分开讨论,第一是POST请求方式,第二是GET方式。

1.POST请求方式

无论哪个版本的tomcat服务器,当发送post请求时都可能出现中文乱码问题。在POST方式请求中,解决乱码有两种方式,

  • 方式一:通过一个方法setCharacterEncoding(编码字符集)来解决:实例如下:

    @WebServlet("/demo06")
    public class ServletDemo06 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 设置请求字符集
            request.setCharacterEncoding("UTF-8");
            String name = request.getParameter("name");
            System.out.println("name = " + name);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
    }
    
  • 方式二:通过String相关方法来解决

    @WebServlet("/demo06")
    public class ServletDemo06 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String name = request.getParameter("name");
            byte[] by = name.getBytes("ISO-8859-1");
            name = new String(by,"utf8");
            System.out.println("name = " + name);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
    }
    

2.GET方式请求中文乱码问题

GET方式请求的正文是在地址栏中,在Tomcat8.5版本及以后,Tomcat服务器已经帮我们解决了,所以 不会有乱码问题了。 而如果我们使用的不是Tomcat服务器,或者Tomcat的版本是8.5以前,那么GET方式仍然会有乱码问 题,解决方式如下:(以下代码了解即可,因为我们现在使用的是Tomcat9.0.27版本)

  • 方式一:通过String相关方法解决代码如上POST方式二

  • 方式二:通过修改配置文件解决

    修改 server.xml 添加 URIEncoding=UTF-8
    

1.7 请求转发

  • 概述:一个web组件将未处理完的请求 通过tomcat转交给另一个web组件进行处理。
  • 特点:
    • 1、浏览器地址栏没有变化
    • 2、请求转发是一次请求
    • 3、他们共享Request域中的数据
    • 4、可以转发到WEB-INF目录下,因为一般情况下,WEB-INF目录是没有办法直接进行访问的,但是可以通过请求转发跳转到WEB-INF目录中。
    • 5、不能访问工程以外的资源
@WebServlet("/demo07")
public class ServletDemo07 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet07....");
        request.getRequestDispatcher("/demo08").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

@WebServlet("/demo08")
public class ServletDemo08 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet08....");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}
  • 执行流程图:

JavaWeb - 10 请求与响应相关操作_第3张图片

1.8 ServletRequest域对象

  • ServletContext域对象的作用范围是整个应用; ServletRequest域对象作用范围是当前请求.
  • 域对象的三个方法:setAttribute , getAttribute ,removeAttribute
@WebServlet("/demo09")
public class ServletDemo09 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet09....");
        request.setAttribute("msg", "helloworld");
        //请求转发
        request.getRequestDispatcher("demo10").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

@WebServlet("/demo10")
public class ServletDemo10 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet10....");
        Object msg = request.getAttribute("msg");
        System.out.println("msg = " + msg);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

1.9 请求包含

在实际开发中,我们可能需要把两个Servlet的内容合并到一起来响应浏览器,而同学们都知道HTTP协 议的特点是一请求,一响应的方式。所以绝对不可能出现有两个Servlet同时响应方式。那么我们就需要 用到请求包含,把两个Servlet的响应内容合并输出。我们看具体使用示例:

@WebServlet("/demo11")
public class RequestDemo11 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.getWriter().write("I am request RequestDemo11 ");
        //1.拿到请求调度对象
        RequestDispatcher rd = request.getRequestDispatcher("/RequestDemo09");
        //2.实现包含的操作
        rd.include(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        doGet(request, response);
    }
}

2.响应对象(ServletResponse)

2.1 响应对象的概述

  • 一样我们首先看看官方文档的描述

    Defines an object to assist a servlet in sending a response to the client. The servlet container creates a ServletResponse object and passes it as an argument to the servlet's service method. 
    定义一个对象,以帮助servlet向客户端发送响应。servlet容器创建一个ServletResponse对象,并将其作为参数传递给servlet的服务方法。
    
    To send binary data in a MIME body response, use the ServletOutputStream returned by getOutputStream(). To send character data, use the PrintWriter object returned by getWriter(). To mix binary and text data, for example, to create a multipart response, use a ServletOutputStream and manage the character sections manually. 
    要在MIME体响应中发送二进制数据,请使用getOutputStream()返回的ServletOutputStream。要发送字符数据,请使用由getWriter()返回的PrintWriter对象。例如,要混合二进制和文本数据,创建多部分响应,可以使用ServletOutputStream并手动管理字符部分。
    
    The charset for the MIME body response can be specified explicitly using the setCharacterEncoding(java.lang.String) and setContentType(java.lang.String) methods, or implicitly using the setLocale(java.util.Locale) method. Explicit specifications take precedence over implicit specifications. If no charset is specified, ISO-8859-1 will be used. The setCharacterEncoding, setContentType, or setLocale method must be called before getWriter and before committing the response for the character encoding to be used. 
    MIME主体响应的字符集可以显式地使用setCharacterEncoding(java.lang.String)和setContentType(java.lang.String)方法指定,或者隐式地使用setLocale(java.util.Locale)方法指定。显式规范优先于隐式规范。如果不指定字符集,则使用ISO-8859-1。setCharacterEncoding、setContentType或setLocale方法必须在getWriter和提交要使用的字符编码的响应之前被调用。
    
    See the Internet RFCs such as RFC 2045 for more information on MIME. Protocols such as SMTP and HTTP define profiles of MIME, and those standards are still evolving.
    有关MIME的更多信息,请参阅因特网RFC,如RFC 2045。诸如SMTP和HTTP之类的协议定义了MIME的概要文件,而这些标准仍在发展中。
    
  • ServletResponse对象支持响应给浏览器,由服务器创建ServletResponse对象,通过service方法返回给浏览器。

  • 通过getOutputStream方法获取ServletOutputStream对象用于发送字节数据, 通过 getWriter方法获取PrintWriter对象发送字符数据, 如果既有字节数据也有字符数据, 还是通过 ServletOutputStream对象.

  • 通过setCharacterEncoding方法和setContentType方法共同设置响应体字符集, 如果不设置 默认是iso8859-1 . 字符集的设置应该在发送响应之前.

  • 响应,它表示了服务器端收到请求,同时也已经处理完成,把处理的结果告知用户。简单来说,指的就 是服务器把请求的处理结果告知客户端。在B/S架构中,响应就是把结果带回浏览器。 响应对象,顾名思义就是用于在JavaWeb工程中实现上述功能的对象。

2.2 响应对象类的继承结构图

JavaWeb - 10 请求与响应相关操作_第4张图片

我们在叙述ServletResponse时涉及的响应对象都是和HTTP协议相关的。即使用的是HttpServletResponse接口的实现类。 这里有人可能会产生疑问,我们在使用Servlet时,需要定义一个类,然后实现Servlet接口(或者 继承它的实现类)。现在我们想要实现响应功能,要不要定义一个类,然后实现HttpServletResponse 接口呢? 此问题的答案是否定的,我们无需这么做。我们只需要在自己写的Servlet中直接使用即可,因为这个对 象的实现类是由Tomcat提供的,无须我们自定义。同时它还会帮我们把对象创建出来并传入doGet和 doPost方法中。

2.3 响应对象的方法

在HttpServletResponse接口中提供了很多方法,接下来我们通过API文档,来了解一下这些方法。

JavaWeb - 10 请求与响应相关操作_第5张图片

常见的状态码:

状态码 描述
200 执行成功
302 它和307一样 都是用于重定向的状态码。只是307目前已不再使用
304 请求资源未改变,使用缓存。
400 请求错误。最常见的就是请求参数有问题
404 请求资源未找到
405 请求方式不被支持
500 服务器运行内部错误

状态码首位代表的含义

状态码 描述
1xx 消息
2xx 成功
3xx 重定向
4xx 客户端错误
5xx 服务器错误

代码实现-内容输出

/**
 * 内容输出
 * getWriter() : 获取PrintWriter对象,字符打印输出值
 */
@WebServlet("/demo11")
public class ServletDemo11 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet11....");

        response.getWriter().write("1234");

        response.getWriter().print("1234");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

2.4 响应输出中文乱码问题

字节输出流中文乱码问题

@WebServlet("/demo12")
public class ServletDemo12 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet12....");
        /**
         * 问题:
         * String str = "字节流中文乱码问题";
         * 使用字节流输出,会不会产生中文乱码?
         * 答案:
         * 会产生乱码
         * 原因:
         * String str = "字节流中文乱码问题"; 在保存时用的是IDEA创建文件使用的字符集UTF8。
         * 到浏览器上显示,chrome浏览器和ie浏览器默认的字符集是GB2312(其实就是GBK),存和
         取用的不是同一个码表,就会产生乱码。
         *
         * 引申:
         * 如果产生了乱码,就是存和取用的不是同一个码表
         * 解决办法:
         * 把存和取的码表统一。
         */
        String str = "字节流输出中文的乱码问题";//UTF-8的字符集,此时浏览器显示也需要使用UTF-8的字符集。
        //1.拿到字节流输出对象
        ServletOutputStream sos = response.getOutputStream();
        /**
         * 解决办法:
         * 第一种解决办法:
         * 修改浏览器的编码,使用右键——编码——改成UTF-8。(不推荐使用,我们的应用尽量不
         要求用户取做什么事情)
         * ie和火狐浏览器可以直接右键设置字符集。而chrome需要安装插件,很麻烦。
         * 第二种解决办法: (不建议使用,因为不好记)
         * 向页面上输出一个meta标签,内容如下: 
         * 其实它就是指挥了浏览器,使用哪个编码进行显示。
         * 第三种解决办法:
         * 设置响应消息头,告知浏览器响应正文的MIME类型和字符集
         * response.setHeader("Content-Type","text/html;charset=UTF-8");
         * 第四种解决办法:我们推荐使用的办法
         * 它的本质就是设置了一个响应消息头
         * response.setContentType("text/html;charset=UTF-8");
         */
        //第二种解决办法:sos.write("".getBytes());
        //第三种解决办法:response.setHeader("Content-Type","text/html;charset=UTF8");
        //第四种解决办法:
        response.setContentType("text/html;charset=UTF-8");
        //2.把str转换成字节数组之后输出到浏览器
        sos.write(str.getBytes("UTF-8"));

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

字符输出流中文乱码问题

@WebServlet("/demo13")
public class ServletDemo13 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("DeoServlet13....");
        String str = "字符流输出中文乱码";
        //response.setCharacterEncoding("UTF-8");
        //设置响应正文的MIME类型和字符集
        response.setContentType("text/html;charset=UTF-8");
        //1.获取字符输出流
        PrintWriter out = response.getWriter();
        //2.使用字符流输出中文
        /**
         * 问题:
         * out.write(str); 直接输出,会不会产生乱码
         * 答案:
         * 会产生乱码
         * 原因:
         * 存用的什么码表:UTF-8
         * 在浏览器取之前,字符流PrintWriter已经获取过一次了,PrintWriter它在取的时候出现了乱码。
         * 浏览器取默认用的是GBK。(本地系统字符集)
         *
         * UTF-8(存)————>PrintWriter ISO-8859-1(取) 乱
         * PrintWirter ISO-8859-1(存)————>浏览器 GBK(取) 乱
         *
         * 解决办法:
         * 改变PrintWriter的字符集,PrintWriter是从response对象中获取的,其实设置response的字符集。
         * 注意:设置response的字符集,需要在拿流之前。
         * response.setCharacterEncoding("UTF-8");
         *
         * response.setContentType("text/html;charset=UTF-8");
         * 此方法,其实是做了两件事:
         * 1.设置响应对象的字符集(包括响应对象取出的字符输出流)
         * 2.告知浏览器响应正文的MIME类型和字符集
         */
        out.write(str);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

2.5 设置响应头-定时刷新/跳转

通过操作响应头Refresh来定时跳转.

@WebServlet("/demo14")
public class ServletDemo14 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String str = "用户名和密码不匹配,2秒后转向登录页面...";
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.write(str);
        //定时刷新,其实就是设置一个响应消息头
        response.setHeader("Refresh", "2;URL=/index.html");//Refresh设置的时间单位是秒,如果刷新到其他地址,需要在时间后面拼接上地址
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
    }
}

还有一种设置方式,但不属于后端相关知识点,属于前端。就是在HTML页面中的head标签中添加如下方式

<meta http-equiv="Refresh" content="2;url=index.html">

效果是一样的。

2.6 请求重定向

  • 概述:是指客户端给服务器发请求,然后服务器回传给客户端一个新地址,让客户端去访问这个新地址。(因为之前的地址可能已经被废弃)
  • 特点:
    • 1、浏览器地址栏会发生变化
    • 2、请求重定向是两次请求
    • 3、不共享Request域中的数据
    • 4、不能访问WEB-INF下的资源
    • 5、可以访问工程外的资源(比如百度)
  • 重定向有两种实现方式

代码实现一:

@WebServlet("/demo15")
public class ServletDemo15 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ServletDemo15...");
        // 设置响应状态码
        response.setStatus(301);
        // 操作响应头,进行重定向
        response.setHeader("Location","demo11");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

代码实现二 :

@WebServlet("/demo16")
public class ServletDemo16 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ServletDemo16...");

        // 请求重定向
        response.sendRedirect("demo11");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

执行过程
JavaWeb - 10 请求与响应相关操作_第6张图片

2.7 消息请求头响应对象-文件下载

开发步骤:

  1. 获取文件下载名称
  2. 告知浏览器文件下载格式
  3. 告知浏览器弹出一个下载窗口并设置编码
  4. IO流拷贝

代码演示

@WebServlet("/demo17")
public class ServletDemo17 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.获取文件下载名称
        String filename = request.getParameter("filename");

        // 2.告知浏览器文件下载格式
        String mimeType = getServletContext().getMimeType(filename);
        response.setHeader("Content-Type",mimeType);

        // 3.告知浏览器弹出一个下载窗口,并且设置编码格式
        String newFileNam = URLEncoder.encode(filename, "utf-8");
        response.setHeader("Content-Disposition" ,"attachment;filename="+newFileNam);

        // 4.IO流拷贝
        // 4.1 获取文件部署路径
        String realPath = getServletContext().getRealPath("img/" + filename);

        // 4.2 获取下载文件的输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(realPath));

        // 4.3 获取字节输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(response.getOutputStream());

        int len = 0;
        byte[] bytes = new byte[8192];

        // 4.4 循环输出
        while ((len = bufferedInputStream.read(bytes)) != -1){
            bufferedOutputStream.write(bytes,0,len);
        }

        bufferedInputStream.close();
        bufferedOutputStream.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

3.其它问题及注意细节

3.1 请求重定向与请求转发的区别

相同点 : 都是用来做资源跳转的。

不同点

请求转发 请求重定向
一个web组件将未处理完的请求 通过tomcat转交给另一个web组件进行处理。 是指客户端给服务器发请求,然后服务器回传给客户端一个新地址,让客户端去访问这个新地址。
浏览器地址不会改变 浏览器地址发生变化
发送一次请求 发送两次请求
可以使用ServletRequest域中共享数据 不可以使用ServletRequest域中共享数据
能访问WEB-INF下的资源 不能访问WEB-INF下的资源
不能访问工程以外的资源 能访问工程以外的资源

3.2 注意的细节问题

请求转发的注意事项:负责转发的Servlet,转发前后的响应正文丢失,由转发目的地来响应浏览器。

请求包含的注意事项:被包含者的响应消息头丢失。因为它被包含起来了。

你可能感兴趣的:(JavaWeb相关内容,servlet,java,服务器)