![cover](https://img-blog.csdnimg.cn/411dbbd2dc8b4d42929d043f6d23026f.png)
Servlet请求和响应
Java打卡:第100天javaWeb — Servlet内容导航Servlet从请求中获取其他信息getRequestURLgetRequestURIgetContextPathgetMethodgetRemoteAddrgetServlePathgetPathInfo请求中文乱码GET和POST不同使用setCharacterEncoding解决POST提交乱码Get和Post都可以;收集字节
Java打卡:第100天
javaWeb — Servlet
内容导航
Java EE
Servlet收尾
100天呐🎉,还是蛮感慨的,磕磕碰碰,终于到了这一步了,虽然JSP技术不是那么新了,但是我奉行存在即合理,还是还看看这种动态网页技术
Servlet
昨天最后分享的请求的域属性空间可以携带数据,通过当前ServletRequest的分配器dispatcher将请求req转发给其他的servlet;这些属性是这些Servlet共享的;与Complex的attribute类似;但是Complex的属性是该应用程序的所有的Servlet共享;这里只是分配到的Servlet共享【一个是请求Request的域属性,一个是环境Complex
//TestServlet
package cfeng;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet; //使用注解进行动态绑定
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(description = "this is just a test class", urlPatterns = { "/TestServlet" })
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("author", "Cfeng");
request.setAttribute("address", "Peking");
request.getRequestDispatcher("/some").forward(request, response);//将对现路径的请求转发给另外的路径,method不变
System.out.println(this.getServletName() +" : " + this.getServletContext().getAttribute("school"));
System.out.println(this.getServletName() +" : " + this.getServletContext().getAttribute("class"));
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);//这里POST提交的时候也会执行GET中的方法
}
}
//SomeServlet
package cfeng;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(description = "just for testing", urlPatterns = { "/some" })
public class someServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(this.getServletName() + ": author = " + request.getAttribute("author"));
System.out.println(this.getServletName() + ": address = " + request.getAttribute("address"));
this.getServletContext().setAttribute("school", "Pecking university");
this.getServletContext().setAttribute("class", "hc2001");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
这里客户端进行访问的时候,请求先到达TestServlet;到达后Test的getServletDisptcher获得调配器,使用器forward将请求传递给SomeServlet执行,SomeServle的doPost方法执行完之后,跳转回原来的TestServlet继续执行调配下面的代码
信息: 已完成重新加载名为/Test的上下文
cfeng.someServlet: author = Cfeng
cfeng.someServlet: address = Peking
cfeng.TestServlet : Pecking university
cfeng.TestServlet : hc2001
这里还可以使用getAttributeNames方法
Enumeration<String> attributeNames = request.getAttributeNames();
while(attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
System.out.println(name + " == " + request.getAttribute(name));
}
这样可以看到还输出了原来自带的域属性例如path
javax.servlet.forward.request_uri == /Test/TestServlet
javax.servlet.forward.context_path == /Test
javax.servlet.forward.servlet_path == /TestServlet
javax.servlet.forward.mapping == org.apache.catalina.core.ApplicationMapping$MappingImpl@10667a58
address == Peking
author == Cfeng
除了get和set方法,对于域属性还有remove方法,可以删除域属性
从请求中获取其他信息
之前已经了解到request相当于就是封装的整个请求的内容;包括请求行,请求头,空白行,请求正文;其中请求正文的name可以通过getParameter获取到,其他的内容也有相应的get方法进行获取
getRequestURL
获取请求的URL,就是地址栏中输入的URL;这是StringBuffer类型的
getRequestURI
获取请求的URI, URL去掉协议以及主机
,统一资源identifier; 是String类型的
getContextPath
获取当前Web项目的根路径,相当于就是创建整个项目的路径,String类型 因为环境对应的就是一个应用程序,所以就可以用comtext代指一个项目
getMethod
获取请求的提交方式;post或者get
getRemoteAddr
remote 遥远的 获取客户端的IP地址; 获取远程的【客户端】的地址
getServlePath
获取注册的url-pattern中匹配的精确的路径的部分
getPathInfo
获取注册的url-pattern中匹配的非精确的路径的部分,也就是对应的通配符的部分
package cfeng;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet; //使用注解进行动态绑定
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(description = "this is just a test class", urlPatterns = { "/TestServlet" })
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/some").forward(request, response);//将对现路径的请求转发给另外的路径,method不变
//获取请求的请求的URL
StringBuffer url = request.getRequestURL();
System.out.println("url = " + url);
//获取请求的请求行的 URI
String uri = request.getRequestURI();
System.out.println("uri = " + uri);
//获取web程序的根路径
String contextPath = request.getContextPath();
System.out.println("contextPath = " + contextPath);
//获取请求的url-pattern的精确部分
String servletPath = request.getServletPath();
System.out.println("servletPath = " + servletPath);
//获取请求的url-pattern的非精确部分,如果都是精确的,那就是null
String pathInfo = request.getPathInfo();
System.out.println("pathinfo = " + pathInfo);
//获取客户端ip地址【远程的地址】;remote都代表的是远程,也就是客户端;还可以获取端口post
String clientIP = request.getRemoteAddr();
System.out.println("clientIP = " + clientIP);
System.out.println("clientPort = " + request.getRemotePort());
System.out.println("clientUser = " + request.getRemoteUser());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);//这里POST提交的时候也会执行GET中的方法
}
}
//上面演示了所有的获取的方法
url = http://localhost:8080/Test/TestServlet
uri = /Test/TestServlet //去除协议、主机和端口
contextPath = /Test //项目的根地址
servletPath = /TestServlet
pathinfo = null //这里的url-pattern是精确的路径,没有通配的,没有pathInfo
clientIP = 0:0:0:0:0:0:0:1
clientPort = 59305 //用户的端口
clientUser = null //这里没有user
请求中文乱码
当使用表单采集信息的时候,如果信息中有中文,会产生乱码的问题【用户名输入张三;servlet获取到的是 user = ???】
这是什么原因导致?
不管是什么字符编码方式,一旦浏览器经过Http协议传输,这些数据都是以字节的方式传给服务器。因为Http协议使用的是TCP传输协议,【TCP协议是面向连接的,可靠的,基于字节流的、端对端的通信协议】,在请求中,字节都是以%开头,以十六进制出现 当用户通过服务器提交一个UTF-8编码格式的两个字符的中文时,浏览器会将两个中文转化为6个字节 , 一个UTF汉字占用三个字节,也就是形成6个类似%8D之类的字节形式,并传送给Tomcat服务器
//可以用watch看一下 输入用户名张三 age 29
user=%E5%BC%A0%E4%B8%89&age=29 可以发现一共有6个字节
Tomcat不知道这些字节是什么编码方式,所以就按照默认的ISO-8859-1的格式进行编码到控制台显示,所以会出现乱码【这种编码不支持汉字】
GET和POST不同
GET的请求的参数出现在请求行中的URI中,而POST的请求参数出现在请求正文中
Tomcat9 中使用GET方式已经解决了中文乱码问题; URL中会显示中文,但是传输基于的是TCP协议,这里的提交的还是字节流
使用setCharacterEncoding解决POST提交乱码
POST的乱码Tomcat解析请求正文的字节使用的是ISO的方式,不支持正文,使用ServletRequest的SetCharacterEncoding将编码方式改成UTF-8就可以解决乱码问题
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
request.getRequestDispatcher("/some").forward(request, response);//将对现路径的请求转发给另外的路径,method不变
System.out.println("user = " + request.getParameter("user"));
System.out.println("age = " + request.getParameter("age"));
}
息: 已完成重新加载名为/Test的上下文
user = 张三
age = 29
但是这里还没有解决GET的乱码问题,虽然9版本会自动解决,但是低版本不会,那么又要如何?
- 在Tomcat的server.xml配置文件增加一个URIEncoding
这里对于低版本的Tomcat,就在Tomcat的conf文件夹中的server.xml文件中的连接器connector标签中加上一个URIEncoding = "UTF-8"就可以解决问题
Get和Post都可以; 收集字节码,使用String的编码方式来解析【转、解码】
这是一种万能的解决乱码问题的方案,就是先获取字节码数组,使用String的构造器获得特定格式编码的数据
- 首先按照特定的编码格式接收字节码
- new String创建按照特定的格式编码
这就是使用到了String的编码和反编码,在面试题中分享过
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/some").forward(request, response);//将对现路径的请求转发给另外的路径,method不变
//接收到user,其字符编码格式为ISO8859-1
String user = request.getParameter("user");
//打散:将user字符串按照原编码方式进行打散 【转码】
byte[] bytes = user.getBytes("ISO8859-1");
//组装 : 将bytes字节数组按照指定编码方式进行组装,组装为String 【特定方式解码】
user = new String(bytes, "UTF-8");
System.out.println("user = " + user);
}
user = 李十三
user=%E6%9D%8E%E5%8D%81%E4%B8%89&age=29
这里一共有9个字节,当进行转码并解码之后就可以得到正确的中文编码
麻烦的地方在于需要对每一个参数进行转码和解码,如果确定是POST,就直接setCharacterEncoding
HttpServletResponse
服务器收到请求之后,会对每一个请求创建一个Request对象进行封装,同时也有一个Response对象,需要获得客户端的请求的信息就是对Request进行操作,但是如果要向客户端发送数据,那么就需要通过Reponse
向客户端发送数据getWriter
ServletReponse有一个方法为getWriter,可以获取到一个输出流对象PrintWriter,该输出流是专门向客户端浏览器输出字符数据的,标准输出流
响应是要响应到浏览器界面的,所以一个响应就对应一个输出流,输出流输出数据有多种方式;可以通过append或者write方法,但是这里都是不会自动换行的;还有就是print和println;println可以换行
//注意需要重启服务器
//获取输出流对象
PrintWriter out = response.getWriter();
out.println("hello! welcome to Cfeng.com!");
out.append("hello");
out.write("hello,cfeng");
虽然这里是一个流,之前IO中提到,为了防止资源浪费,经常会及时关闭输出流;但是这里的标准输出流和响应式成对出现的,当响应reponse没有的时候,流就会被服务器自动关闭;并且如果要对流进行操作比如fitter,是不能关闭输入流的。
Response中文乱码 setContentType
这里当向浏览器界面显示中文的时候,如果不处理又会乱码,会变成???, 之前的setCharacterEncoding只能解决Request的乱码问题
使用的方法为setContentType,设置内容格式,其实就是MIME类型
- MIME类型 : 媒体类型 Multipuporse Internet Mail Extension ; 是一种标准,用来表示各种文件,包括文档、文件、视频、音频、字节流的
性质和格式
比如video/mp4 ; image/jpg text/html
所以这里同时就可以设置内容的字符编码方式,就是响应体的编码方式
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("hello,C风官网");
这样输出的就是正常的,这里的MIME格式为text文件中的HTML格式;所以下面输入的其实都是text;也就是输入的都是HTML格式的text;在html分享时,没有标签就会当作普通的文本
那么SerCharacterE你coding怎么回事
setCharacterEncoding 设置的是MIME类型的charset;也就是说这个方法要想起作用,就必须有setContentType方法;所以一半很少采用,就直接使用之前的方法,直接定义好charset就可;如果没有设置,就可以使用这个方法进行设置
response.setContentType("text/html"); //这里没有设置编码类型
response.setCharacterEncoding("UTF-8"); //可以给上面的MIME类型设置字符编码方式
并且这个方法只能在创建输出流之前使用,之后就不起作用了,因为流的编码方式已经确定了
这里的MIME类型,在web引用中进场使用到,type 有text/css text/javascript ……
因为这里的MIME是text/html,所以输出的字符串是直接按照html解析的
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("<font color='red'> 你好,C风<font>");
这里输出的内容就变成红色了,因为解析了字体标签
request请求转发和response重定向
上面已经分享了请求request和响应response;其中请求就是将客户端发送的请求封装为一个对象;可以通过get方法获取到请求对象中的内容;响应对应一个输出流,输出流可以不手动关闭,因为响应的时间短暂,自动关闭,解决乱码问题使用转码解码;response使用的是setContentType;设置MIME类型
客户端来的请求到达一个资源之后,从资源1跳转到资源2的方式有重定向和请求转发
请求有一个方法为getServletDisptcher可以获得一个servlet调配器,通过该对象的forward方法可以完成请求转发功能; 还可以通过Response的SendRedirect,完成重定向功能 【redirect 使改变方向 重定向】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GRE5a4Dh-1640098027115)(https://tse1-mm.cn.bing.net/th/id/R-C.3deb487d5d8c3857284f900303ff7345?rik=SpgUSmXynjbZvw&riu=http%3a%2f%2fwww.monkey1024.com%2fwp-content%2fuploads%2f2018%2f03%2f%e8%bd%ac%e5%8f%91%e4%b8%8e%e9%87%8d%e5%ae%9a%e5%90%91.png&ehk=18dbhmA4pkGl7qfcC%2f%2fkrzh3sfLJB38rtOZo6g%2fzw5k%3d&risl=&pid=ImgRaw&r=0)]
可以直观看出来,两者还是有很大的区别,请求转发只有一次请求,一次响应【反馈】,服务器内转发;
重定向n次请求和n次响应;无状态协议,请求是不相同的,所以数据不能共享,就是域属性后面的资源不能访问;服务器外转发; 所以这里还是比转发麻烦的,但是后面的请求发送是服务器自动发送的,用户感知不到;用户的感知还是一次请求,一次响应;但是内部的执行过程不一样
//请求转发,只有一次请求,所以都是相同的
//这里请求转发因为只有一次请求,所以数据共享,特别是域属性,和请求中的内容,也就是request是相同的,parameter相同;通过forward方法就可以发现是将当前servlet的request和response给了下一个资源
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user = request.getParameter("user");
//解决乱码问题
user = new String(user.getBytes("ISO8859-1"),"UTF-8"); //经典问题,以字面量在堆中新创建一个对象
System.out.println("user = " + user);
// response.setContentType("text/html;charset=UTF-8");
response.getWriter().append("你好,java学习者");
request.getRequestDispatcher("/some").forward(request, response); //相当于给到下一个的request和response都是这个
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);//这里POST提交的时候也会执行GET中的方法
}
}
//上面是TestServlet,就是最开始跳转的界面
@WebServlet(description = "just for testing", urlPatterns = { "/some" })
public class someServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().append("<br>你好,Cfeng!");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
//这里的some是请求转发的目标资源
经过反复的尝试可以发现
- response是只有一个,在Test中设置了MIME格式,在some中就没有乱码;如果注释掉就会乱码; 并且test的输出流printwriter不起作用,起作用的是后面的some 一个response只有一个wirter
重定向是resonse的sendRedirect方法
//重定向其实就是新的请求了,相当于以一个新的请求触发Servlet的Service服务
//所以最开始的parameter就没有了
response.sendRedirect("some"); //重定向,服务器自动进行外部跳转【新的请求】
System.out.println("user = ” + request.getParameter("user"));
user = null; //这里 就是因为重定向的请求不一样了,数据不共享
重定向中填入地址不需要加上/;而请求转发则要加上/
请求转发的响应是最开始的servlet的response,地址栏为第一个的URL; 但是重定向多次请求,多次响应,最后发出来的是最后一个servlet的response,也就是最后一个的URL 因为请求转发不断将第一个资源的response和request传递给下一个资源 ;
如何解决重定向数据传输问题
重定向的方法中加的是项目后面的部分;不需要加上/;其实重定向后面的请求的触发方式都是GET,因为POST就两种,一种是HTML表单,一种是AJAX;所以如何携带数据; 这里就是GET的携带数据就是在URL中直接显示的,那么也是属于资源的一部分
那就是直接在资源中使用?name=value&name=value…… 这样就可以携带数据了;
response.sendRedirect("some?user=" + user); //这就是get方式提交参数
request.setCharacterEncoding("UTF-8");
System.out.println("user = " + request.getParameter("user"));
这里就输出了
user = 张三
重定向乱码问题 URLEncoder
重定向数据传输中文乱码http://localhost:8080/Test/some?user=?? 这里就可以看出出现了乱码,使用new String()没有用处
之前说过,HTTP进行数据传输的时候,是基于TCP协议的,TCP是使用的字节流进行传输的【也就是byte[],而不是String】,这里上面使用的是UTF-8编码的字符串;这里传输的必须是URL的字节流,不是getbytes打散的;要使用工具类URLEncoder进行编码成字节流,再使用URLDecoder转码;
转码和解码要成对使用,但是在后面的资源位置解码之后,得到的不是UTF-8类型的String;而是一个ISO8859-1类型的,这里还要再使用new String进行再转码和解码
感觉有点长,先这样吧,剩下的明天再发📕
更多推荐
所有评论(0)