服务器处理请求

request和response都是服务器创建的对象
参考文章Http协议

服务器处理请求流程
服务器每次收到请求时,都会为这个请求开辟一个新的线程。
服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体。
服务器还会创建response对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应。

一、request

request封装了客户端所有的请求
request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。

request对象的功能

  • 1)获取Http的请求头
    • String getHeader(String name)
      获取指定名称的请求头
    • int getIntHeader(String name)
      获取值为int类型的请求头
    • Enumeration getHeaderNames()
      获取所有请求头名称

小栗子:
获取客户端的IP地址、获取请求方式、获取User-Agent,得到客户端的信息(操作系统 浏览器)。

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 获取客户端的IP地址、获取请求方式、获取User-Agent,得到客户端的信息(操作系统 浏览器)。
 *
 */
public class AServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String addr = request.getRemoteAddr();//获取客户端的IP地址
        
        response.sendRedirect("http://www.baidu.com");
        if(true) return;
        
        System.out.println("IP: " + addr);
        System.out.println("请求方式:" + request.getMethod());//获取请求方式
        String userAgent = request.getHeader("User-Agent");//获取名为User-Agent的请求头!
        // 是否包含Chrome,如果包含,说明用户使用的是google浏览器
        if(userAgent.toLowerCase().contains("chrome")) {
            System.out.println("您好:" + addr + ", 你用的是谷歌");
        } else if(userAgent.toLowerCase().contains("firefox")) {
            System.out.println("您好:" + addr + ", 你用的是火狐");
        } else if(userAgent.toLowerCase().contains("msie")) {
            System.out.println("您好:" + addr + ", 你用的是IE");
        }
    }
}
运行结果
  • 2)获取请求url
    例如地址:http://localhost:8080/visitCount/AServlet?username=xxx&pwd=yyy
    • String getScheme()
      获取协议
      http
    • String getServerName()
      获取服务器名
      localhost
    • String getServerPort()
      获取服务器端口
      8080
    • String getContextPath()
      获取项目名
      /visitCount
    • String getServletPath()
      获取Servlet路径
      /AServlet
    • String getQueryString()
      获取参数部分,即问号后面的部分
      username=xxx&password=yyy
    • String getRequestURI()
      获取请求URI,即项目名+Servlet路径
      /visitCount/AServlet
    • String getRequestURL()
      获取请求URL,即不包含参数的整个请求路径
      http://localhost:8080/visitCount/AServlet
获取请求url
  • 3)获取请求参数
    请求参数是由客户端发送给服务器的
    有可能是在请求体中(POST),也可能是在URL之后(GET)

    • String getParameter(String name)
      获取指定名称的请求参数值,适用于单值请求参数
    • String[] getParameterValues(String name)
      获取指定名称的请求参数值,适用于多值请求参数
    • Enumeration<String> getParameterNames()
      获取所有请求参数名称
    • Map<String,String[]> getParameterMap()
      获取所有请求参数,其中key为参数名,value为参数值。
  • 4)请求转发和请求包含
    在一个请求链中,涉及到多个Servlet
    RequestDispatcher rd = request.getRequestDispatcher("/MyServlet");
    使用request获取RequestDispatcher对象,方法的参数是被转发或包含的Servlet的Servlet路径

    • 请求转发
      rd.forward(request,response);
    • 请求包含
      rd.include(request,response);

在什么时候使用请求转发和请求包含
需要多个Servlet协作才能完成,需要在一个Servlet跳到另一个Servlet。

  • 一个请求跨多个Servlet,需要使用转发和包含。
  • 请求转发:由下一个Servlet完成响应体!当前Servlet可以设置响应头!(针对第一个Servlet来说:留头不留体)
  • 请求包含:由两个Servlet共同未完成响应体!(都留)
  • 无论是请求转发还是请求包含,都在一个请求范围内,使用同一个request和response。

小栗子:请求转发
OneServlet.java

package cn.itcast.servlet.forward;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 请求转发
 *
 */
public class OneServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("OneServlet...");
        response.setHeader("aaa", "AAA");//设置响应头
        response.getWriter().print("a");//设置响应体

//如果要转发给其他页面做,就不要在本页面做太多动作,不然会抛异常
//如果执行下面注释的代码就会抛异常
//      for(int i = 0; i < 1024 * 24 + 1; i++) {
//          response.getWriter().print("a");//设置响应体
//      }

        //请求转发
        request.getRequestDispatcher("/TwoServlet").forward(request, response);
    }
}


//只保留请求头
内容显示TwoServlet.java的内容
  • 5)request域
    在两个Servlet中通过转发或包含来完成传递中会用到request域
    request是Servlet三大域对象之一。
    request,session,application。这三大域都有一下几种方法
    • void setAttribute(String name, Object value)
      用来存储一个对象,也可以称之为存储一个域属性
    • Object getAttribute(String name)
      用来获取request中的数据,当前在获取之前需要先去存储才行
    • void removeAttribute(String name)
      用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做
    • Enumeration getAttributeNames()
      获取所有域属性的名称

同一请求范围内使用request.setAttribute()、request.getAttribute()来传值!前一个Servlet调用setAttribute()保存值,后一个Servlet调用getAttribute()获取值。

  • 6)请求转发和重定向的区别
    • 请求转发是一个请求一次响应,而重定向是两次请求两次响应
    • 请求转发地址栏不变化,而重定向会显示后一个请求的地址
    • 请求转发只能转发到本项目其他Servlet,而重定向不只能重定向到本项目的其他Servlet,还能定向到其他项目
    • 请求转发是服务器端行为,只需给出转发的Servlet路径,而重定向需要给出requestURI,即包含项目名!
    • 转发的效率比重定向的效率高
    • 需要地址栏发生变化,那么使用重定向!
    • 需要在下一个Servlet中获取request域中的数据,要使用转发!

二、response

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

ServletResponse和HttpServletResponse的区别
ServletResponse是与协议无关的类型
HttpServletResponse是与协议相关的类型

1.response对象的功能

  • 1)发送状态码
    状态码:200表示成功、302表示重定向、404表示客户端错(访问的资源不存在)、500表示服务器端错
    • sendError(int sc)
      发送错误状态码,eg:404,500
    • sendError(int sc, String msg)
      发送错误状态码,携带一个错误信息
    • setStatus(int sc)
      发送成功的状态码,可以用来发送302

小栗子:发送404

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 发送404状态码
 *
 */
public class AServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.sendError(404, "您访问的资源存在,就不给你看!");
    }
}
  • 2)设置响应头信息
    响应头:Content-Type、Refresh、Location等等
    头就是键值对
    • setHeader(String name, String value)(常用)
      适用于单值的响应头,
      例如:response.setHeader("aaa", "AAA");
      名字叫aaa,值是AAA
    • addHeader(String name, String value)
      适用于多值的响应头
      例如:
      response.addHeader("aaa", "A");
      response.addHeader("aaa", "AA");
      response.addHeader("aaa", "AAA");
    • setIntHeader(String name, int value)
      适用于单值的int类型的响应头
      例如:response.setIntHeader("Content-Length", 888);
    • addIntHeader(String name, int value)
      适用于多值的int类型的响应头
    • setDateHeader(String name, long value)
      适用于单值的毫秒类型的响应头
      例如:response.setDateHeader("expires", 1000 * 60 * 60 * 24);
    • addDateHeader(String name, long value)
      适用于多值的毫秒类型的响应头

小栗子:

①发送302,设置Location头,完成重定向

重定向思路图
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 重定向!
 *
 * 用户请求BServlet,然后BServlet响应302,给出Location头
 */
public class BServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("BServlet");
        
        /*
         * 重定向:
         * 1. 设置Location
         * 2. 发送302状态码
         */
//      response.setHeader("Location", "/text/CServlet");
//                                     项目名/Servlet路径(请求url)
//      response.setStatus(302);
        
        //快捷的重定向方法
        response.sendRedirect("/text/CServlet");
    }
}

②定时刷新:设置Refresh头,定时重定向!

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 定时刷新
 * 
 * 设置一个Refresh,它表示定时刷新!
 */
public class DServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*
         * 下面是用来发送响应体!
         */
        PrintWriter writer = response.getWriter();
        writer.print("欢迎XXX登录!5秒钟后会自动跳转到主页!您看到的一定是乱码!");
        /*
         * 设置名为Refresh的响应头
         */
        response.setHeader("Refresh", "5;URL=/test/EServlet");
    }
}
  • 3)响应体
    通常是html、也可以是图片
    • PrintWriter out = response.getWriter()
      向客户端发送字符数据!需要设置编码
    • ServletOutputStream out = response.getOutputStream()
      向客户端发送字节数据

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

小栗子:
使用ServletOutputStream发送字节数据

import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

public class GServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
//使用ServletOutputStream发送字节数据(文本)
/*
        String s = "Hello outputStream";
        byte[] bytes = s.getBytes();
        response.getOutputStream().write(bytes);
*/
//使用ServletOutputStream发送字节数据(图片)
        // 把一张图片读取到字节数组中
        String path = "F:/F/白冰.jpg";
        FileInputStream in = new FileInputStream(path);
//      byte[] bytes = IOUtils.toByteArray(in);//读取输入流内容的字节到字节数组中。
//      response.getOutputStream().write(bytes);
        IOUtils.copy(in, response.getOutputStream());
    }
}
  • 4)重定向
    设置302,设置Location!其中变化的只有Location,所以java提供了一个快捷方法,完成重定向
    • sendRedirect(String location)
      上面的小栗子用过哟!

三、编码

常见字符编码:iso-8859-1(不支持中文)、gb2312、gbk、gb18030(系统默认编码,中国的国标码)、utf-8(万国码,支持全世界的编码,所以我们使用这个)

一般会在以下情况中出现编码问题

  • 响应编码
    服务器发送给客户端数据
    服务器和客户端的编码要一致,不然就会出现乱码

不乱码:
在是用getWriter()方法之前,先调用`response。setContentType("text/html;charset=utf-8");

响应编码
  • 请求编码
    请求数据是由客户端浏览器发送服务器的,请求数据的编码是由浏览器决定的
    页面请求是什么编码的,发送的请求就是什么编码
    服务器端默认使用ISO-8859-1来解码!所以这一定会出现乱码的!因为iso不支持中文!

请求编码处理分为两种:GET和POST
1)GET请求编码处理
tomcat8的默认编码已经是utf-8,所以不用变了
2)POST请求编码处理
String username = new String(request.getParameter("iso-8859-1"), "utf-8");
在获取参数之前调用request.setCharacterEncoding("utf-8");

请求编码

代码说明:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        /*
         * 1. 在获取参数之前,需要先调用request.setCharacterEncoding("utf-8");
         * 2. 使用getParameter()来获取参数
         */
        req.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        System.out.println(username);
    }
}

  • URL编码
    通过页面传输数据给服务器时,如果包含了一些特殊字符是无法发送的。这时就需要先把要发送的数据转换成URL编码格式,再发送给服务器。
    POST请求默认就使用URL编码!tomcat会自动使用URL解码!
    • URL编码:String username = URLEncoder.encode(username, "utf-8");
    • URL解码:String username = URLDecoder.decode(username, "utf-8");
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容