Java Web请求和响应机制

时间:2023-03-08 21:33:52

请求响应流程图

Java Web请求和响应机制

===================

服务器处理请求的流程:

  服务器每次收到请求时,都会为这个请求开辟一个新的线程。

  服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体!

  服务器还会创建response对象,这个对象与客户端连接在一起,它可以用来向客户端发送响应。

===================

response:其类型为HttpServletResponse

  *状态码:200表示成功、302表示重定向、404表示客户端错误(访问的资源不存在)、500表示服务器错误

    >sendError(int sc):发送错误的状态码,例如404、500

    >sendError(int sc, String msg):发送错误的状态码+错误信息

    >sendStatus(int sc):发送成功的状态码,例如302

404案例:

 //404案例
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendError(404,"您访问的资源不存在!!!");
}

  *响应头:Content-Type、Refresh、Location等等

    头就是一个键值对!可能会存在一个头(一个名称,一个值),也可能会存在一个头(一个名称,多个值!)

    >(重要)setHeader(String name, String value):适用于单值的响应头,例如:response.setHeader("aa","AAA");

    >addHeader(String name, String value):适用于多值的响应头

      response.addHeader("aa","A");

      response.addHeader("aa","AA");

      response.addHeader("aa","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);  设置页面过期时间为24小时

    >addDateHeader(String name, long value):适用于多值的毫秒类型的响应头

案例:

  1)发送302,设置Location头,完成重定向!

Java Web请求和响应机制

BServlet

 //重定向1.设置Location头(重定向的地址)2.响应302状态码

 @WebServlet("/BServlet")
public class BServlet extends HttpServlet {
//重定向1.设置Location 2.发送302状态码 @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("BServlet");
// 请求URI s1的值:/项目名/Servlet名
resp.setHeader("Location","/CServlet");
resp.setStatus(302);
}
}

CServlet

 @WebServlet("/CServlet")
public class CServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("CServlet");
}
}

  2)定时刷新:设置Refresh头(你可以把它理解成,定时重定向!)

DServlet

 @WebServlet("/DServlet")
public class DServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("DServlet");
resp.setHeader("Refresh","5;URL=/CServlet");//5秒后重定向到Cservlet
}
}

  3)禁用浏览器缓存:Cache-Control、pragma、expires

FServlet

 @WebServlet("/FServlet")
public class FServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response){
response.setHeader("Cache-Control","no-cache");
response.setHeader("pragma","no-cache");
response.setDateHeader("expires",-1);//过期时间为-1,也就是不缓存
}
}

在index.jsp中有

Java Web请求和响应机制

  4)<meta>标签可以替代响应头:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

  *响应体: 通常是html、也可以是图片!

    >response的两个流:

      <>ServletOutputStream,用来向客户端发送字节数据。ServletOutputStream out=response.getOutputStream();

      <>PrintWriter,用来向客户端发送字符数据!需要设置编码。PrintWriter pw=response.getWriter();

      <>两个流不能同时使用!

      在Interface ServletResponse接口中有方法getOutputStream()和getWriter(),如果同时使用,会抛出IllegalStateException异常

案例:(不同时)使用两个流

  1)使用ServletOutputStream字节流向客户端写字符串和图片

 @WebServlet("/GServlet")
public class GServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*String s="Hello outputStream 谢";
byte[] bytes=s.getBytes();//将字符串转换成字节,存储到byte数组中
resp.getOutputStream().write(bytes);//字节流*/ //响应字节数据,1.把一张图片读取到字节数组中2.使用字节流进行输出
FileInputStream in=new FileInputStream("G://金软软.jpg");
//读取输入流内容的字节到字节数组中
byte[] bytes1= IOUtils.toByteArray(in);
resp.getOutputStream().write(bytes1);
}
}

  2)使用PrintWriter字符流向客户端写内容,需要设置字符编码

 @WebServlet("/HServlet")
public class HServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("谢军帅");
resp.getWriter().print("<br>金泰妍");
}
}

  *重定向:设置302,设置Location!其中变化的只有Location,所以Java提供了一个快捷方法,完成重定向1

     >sendRedirect(String location)方法

 //快捷
resp.sendRedirect("http://www.baidu.com");

request:封装了客户端所有的请求数据!

请求行

请求头

空行

请求体(GET没体)

  *获取常用信息

    >获取客户端IP,request.getRemoteAddr()

    >请求方式,request.getMethod(),可能时POST或GET

  *获取请求头

    >(重要)String getHeader(String name),适用于单值头

    >int getInHeader(String name),适用于单指int类型的请求头

    >long getDateHeader(String name),适用于单值毫秒类型的请求头

    >Enumeration<String> getHeaders(String name),适用于多值请求头

通过User-Agent识别用户浏览器类型

 //演示:获取客户端的IP地址、获取请求方式、获取User-Agent,得到客户端的信息(操作系统,浏览器)
@WebServlet("/Servlet2")
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String remoteAddr = req.getRemoteAddr();
System.out.println("(IPV6)IP:"+remoteAddr);
String method = req.getMethod();
System.out.println("请求方式:"+method);
String userAgent = req.getHeader("User-Agent");
System.out.println("userAgent:"+userAgent);
//先把字符串转换成小写,,然后再看是否包含
if(userAgent.toLowerCase().contains("chrome")){
System.out.println("您好"+remoteAddr+"您使用的是谷歌浏览器");
}else if (userAgent.toLowerCase().contains("firefox")){
System.out.println("您好"+remoteAddr+"您使用的是火狐浏览器");
}else if (userAgent.toLowerCase().contains("msie")){
System.out.println("您好"+remoteAddr+"您使用的时IE浏览器");
}else{
System.out.println("不晓得你用的啥");
}
}
}

防盗链:如果请求不是通过本站的超链接发出的,发送错误状态码404。Refresh这个请求头,表示请求的来源!(比如:从一个页面跳转到另一个页面,都会有Refresh值)

 @WebServlet("/Servlet3")
public class Servlet3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
/*使用Refresh请求头,来防盗链*/
String referer = req.getHeader("Referer");
System.out.println(referer);
if(referer == null || !referer.contains("localhost")){//如果前面的满足的话,后边的就不用判断了
resp.sendRedirect("http://www.baidu.com");
}else{
System.out.println("hello");
}
}
}
/*
Referer这个头可以得到请求的来源;
1.在地址栏中请求的话其值为null
2.在其它页面以链接的形式请求的话其值为URL
*/

  *获取请求URL

    http://localhost:8080/day10_2/AServlet?username=xxx&password=yyy

    >String getScheme():获取协议,http

    >String getServerName():获取服务器名,localhost

    >String getServerPort():获取服务器端口,8080

    >String getContextPath():获取项目名(会经常使用,重要),/day10_2

    >String getServletPath():获取Servlet路径,/AServlet

    >String getQueryString():获取参数部分,即问号后面的部分,username=xxx&password=yyy

    >String getRequestURI():获取请求URI,等于项目名+Servlet路径,/day10_2/AServlet

    >String getRequestURL():获取请求URL,等于不包括参数的整个请求路径,http://localhost:8080/day10_2/AServlet

  *获取请求参数:请求参数是由客户端发送给服务器的!有可能是在请求体中(POST),也可能在URL之后(GET)

    >***String getParameter(String name):获取指定名称的请求参数值,适用于单值请求参数

    >String[]  getParameterValues(String name):获取指定名称的请求参数值,适用于多值请求参数

    >Enumeration<String>  getParameterNames():获取所有请求参数的名称

    >***Map<String,String[] >  getParameterMap():获取所有的请求参数,其中key为参数名,value为参数值。将请求的参数封装到Map中

    案例:超链接参数

    案例:表单数据

  *请求转发和请求包含---dispatcher的英文意思是调度员,来调用别的Servlet

    RequestDispatcher rd=request.getRequestDispatcher("/MyServlet");---->使用request获取RequestDispatcher对象,括号里的参数是被转发或包含的Servlet的servlet路径

    请求转发:***rd.forward(request,response);

    请求包含:rd.include(request,response);

Java Web请求和响应机制

    有时一个请求需要多个Servlet协作才能完成,所以需要在一个Servlet跳到另一个Servlet!

    >一个请求跨多个Servlet,需要使用转发和包含。

    >请求转发:由下一个Servlet完成响应体!当前Servlet可以设置响应头!(留头不留体)

    >请求包含:由两个Servlet共同来完成响应体!(都留)

    >无论是请求转发还是请求包含,都在一个请求范围内!使用同一个request和response!

请求转发示例:

OneServlet:

 public class OneServlet extends HttpServlet {

     public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("OneServlet...");
response.setHeader("aa", "AA");//头
response.getWriter().print("hello OneServlet!");//体 //转发
RequestDispatcher rd=request.getRequestDispatcher("/TwoServlet");
rd.forward(request, response);
}
}

TwoServlet:

 public class TwoServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("TwoServlet...");
response.getWriter().print("hello TwoServlet");
}
}

在浏览器中访问http://localhost:8080/XJS_Servlet3/OneServlet

控制台结果:

OneServlet...
TwoServlet...

浏览器页面结果:

还有OneServlet设置的头也发送到浏览器了

hello TwoServlet 

证明了OneServlet留头不留体

请求包含示例:

Servlet1:

 public class Servlet1 extends HttpServlet {

     public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("Servlet1...");
response.setHeader("aa", "AA");//头
response.getWriter().print("hello OneServlet!");//体 //转发
RequestDispatcher rd=request.getRequestDispatcher("/include/Servlet2");
rd.include(request, response);
}
}

Servlet2:

 public class Servlet2 extends HttpServlet {

     public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("TwoServlet...");
response.getWriter().print("hello TwoServlet");
} }

请求http://localhost:8080/XJS_Servlet3/include/Servlet1的结果:

控制台:

Servlet1...
TwoServlet...

浏览器页面:

hello OneServlet!hello TwoServlet

 结果证明:include留头又留体

  *request域

    Servlet中三大域对象:request、session、application,都有如下三个方法:

    >void setAttribute(String name, Object value)

    >Object getAttribute(String name)

    >void removeAttribute(String name)

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

  *请求转发和重定向的区别

    >请求转发是一个请求一次响应,而重定向是两次请求两次响应

    >请求转发地址栏不变化,而重定向会显示后一个请求的地址

    >请求转发只能转发到本项目的其他Servlet,而重定向不只能重定向到本项目的其他Servlet,还能定向到其他项目

    >请求转发是服务器端行为,只需给出转发的Servlet路径,而重定向需要给出requestURL,即包含项目名!!!

    >请求转发和重定向效率是转发高!因为是一个请求!

      <>需要地址栏发生变化,那么必须使用重定向!

      <>需要在下一个Servlet中获取request域中的数据,必须使用转发!


编码

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

1.响应编码

Java Web请求和响应机制

response.setCharacterEncoding("utf-8");//设置服务器的编码为utf-8
response.setHeader("Content-Type","text/html;charset=utf-8");
//设置响应头,服务器编码格式为utf-8

response设置Content-Type的快捷方法:

response.setContentType("text/html;charset=utf-8");

想不乱码:在使用getWriter()方法之前,先调用下面方法:

      ***response.setContentType("text/html;charset=utf-8");

2.请求编码---所有的乱码都有解决的方法(先在Servlet中获取参数,然后在对参数进行反编码)

一般请求参数,在Servlet中获取时不会出现乱码,如果出现乱码就按照下面方法解决乱码:

Java Web请求和响应机制

  *客户端发送给服务器的请求参数是什么编码:

    客户端首先要打开一个页面,然后在页面中提交表单或点击超链接!在请求这个页面时,服务器响应的编码是什么,那么客户端发送请求时的编码就是什么

  *服务器端默认使用什么编码来解码参数:

    (tomcat8之前的)服务器端默认使用ISO-8859-1来解码!所以这一定会出现乱码的!因为iso不支持中文!

在Servlet中设置request.setCharacterEncoding("utf-8");---然后在获取请求参数,可以防止乱码

Tomcat8默认编码为UTF-8

Java Web请求和响应机制

3.URL编码

  表单的类型:Content-Type:application/x-www-form-urlencoded,就是把中文转换成%后面跟随两位的16进制。

  为什么要用它:在客户端和服务器之间传递中文时需要把它转换成网络适合的方式。

  *它不是字符编码!

  *它是用来在客户端和服务器端之间传递参数用的一种方式!

  *URL编码需要先指定一种字符编码,把字符串解码后,得到byte[] ,然后把小于0的字节+256,再转换成16进制。前面加一个%。

  *POST请求默认就使用URL编码!tomcat会自动使用URL解码!

  *URL编码:String username=URLEncoder.encode(username,"utf-8");

  *URL解码:String username=URLDecoder.decode(username,"utf-8");

1)GET请求中的中文没有URL编码,可能会出现丢失字节

2)使用的是表单,表单自动使用URL编码

3)服务器会自动识别URL编码,然后自动做URL解码

4.路径

  *web.xml中<url-pattern>路径,(叫它Servlet路径!)

    >要么以 “*” 开头,要么以 “/” 开头。

  *转发和包含路径

    >以 “/”开头:相对当前项目的路径,例如:http://localhost:8080/项目名/    request.getRequestdispacher("/BServlet").forward(request,response);

    >不以 “/” 开头:相对当前Servlet路径

  *重定向路径(客户端路径)

    >以“ /”开头:相对于当前主机,例如:http://localhost:8080/,所以需要自己手动添加项目名

  *页面中超链接和表单路径

    >与重定向相同,都是客户端路径!需要添加项目名

    ><form  action="/XJS_Servlet3/AServlet">

    ><a href="/XJS_Servlet3/AServlet">

    >****建议使用以 “/” 开头的路径,即绝对路径!

  *ServletContext获取资源路径

    >相对当前项目目录,即index.jsp所在目录。

  *ClassLoader获取资源路径

    >相对classes目录

    >ClassLoader获取资源时,不能以“/”开头!

  *Class获取资源路径

    >以“/”开头相对classes目录

    >不以"/"开头相对当前.class文件所在目录。