JavaWeb学习笔记(四)之Servlet

Servlet基础下

文件的下载和上传(***重要***)

HTTP请求及HTTP响应中都包含正文部分。HTTP响应的正文部分最常见的是HTML文档,此外还可以是其他任意格式的数据,如图片和声音文件中的数据。同样,HTTP请求的正文部分不仅可以是字符串格式的请求参数,也可以是其他任意格式的数据。

Web服务器只要把特定文件中的数据放到HTTP响应的正文部分,就能向浏览器发送任意格式的文件。同样,浏览器只要把特定文件中的数据放到HTTP请求的正文部分,也能向服务器发送任意格式的文件。

文件下载

文件下载是指把服务器端的文件发送到客户端,Servlet能够向客户端发送任意格式的文件数据。

文件上传

文件上传指把客户端文件发送到服务器端。此时,客户端发送的HTTP请求正文采用 multipart/form-data数据类型,它表示复杂的包含多个子部分的复合表单。

文件上传原理分析:

HTML核心代码

<form method="post" enctype="MULTIPART/FORM-DATA" action=" UploadServlet">

    Choose File:  <input type="file" name="filedata" size="30"/>

    <input type="submit" name="submit" value="upload">

form>

运行显示如下图

此时点击浏览,选择Data.txt文件作为上传文件。

Data.txt中包含如下内容

Test  Dataupload01

Test  Dataupload02

Test  Dataupload03

Test  Dataupload04

此时点击upload按钮,提交给Web服务器。此时HTTP请求信息


分析上传文件的HTTP请求

HTTP请求的正文部分为复合类型,包含两个子部分:文件部分和提交按钮部分。提交请求时,浏览器随机产生了一个字符串形式的边界(boundary)作为HTTP请求头的一部分:

HTTP请求的正文部分的各个子部分之间用边界进行分割。每个子部分由头和正文部分组成,头和正文部分之间用空行分割。如下图

根据以前所学,如果要将Data.txt文件中数据上传到服务器,需要对HTTP请求进行解析,读取其中的边界值,再根据边界值定位到文件部分,进而定位到文件部分的正文部分,再把正文部分的数据保存到本地文件系统中。

此程序可向服务器端上传任意格式的文件数据。一般处理文件部分的正文部分时,会按照字节流而不是字符流处理写到本地文件整。

思路:解析该HTTP请求,将上传的信息保存到服务器。

根据以上分析,不管HTTP请求正文为何种数据类型,Servlet容器都会把HTTP请求包装成一个HttpServletRequest对象。请求正文为”multipart/form-data”数据类型时,Servlet直接从HttpServletRequest对象中解析出复合表单的每个子部分,但工作量依然非常复杂。

使用Apache开源软件组织提供的两个软件包实现文件上传,可以简化操作。

fileupload软件包(commons-fileupload-xxx.jar):负责上传文件的软件包

io软件包(commons-io-xx.jar):负责输入输出的软件包。该包是fileupload包的依赖包。

对于正文部分为”multipart/form-data”类型的HTTP请求,uploadfile软件包把请求正文包含的复合表单中的每个子部分看做是一个FileItem对象。FileItem对象分为两种类型。

Ø  formField:普通表单域类型。表单中的文本框及提交按钮等都是这种类型。

Ø  formField:上传文件类型。表单中的文本域就是这种类型,包含了文本数据。

fileupload涉及的接口或类的解释

Ø  DiskFileItemFactory类实现了FileItemFactory接口,可用于创建DiskFileItem对象的工厂。

Ø  DiskFileItem类实现了FileItem接口,表示基于硬盘的FileItemDiskFileItem能够把客户端上传的文件数据保存到硬盘上,

Ø  ServletFileUpload类为文件上传类,与FileItemFactory关联。是文件上传处理类。可设置上传文件的大小,解析请求对象中的复合表单。

DiskFileItemFactory(可理解为向本地磁盘写数据的IO流对象)

//创建一个基于硬盘的FileItem工厂

DiskFileItemFactory factory = new DiskFileItemFactory();

//设置向硬盘写数据时所用的缓冲区大小.为了提高向硬盘写入数据的效率,特别是大容量数据的效率

factory.setSizeThreshold(2*1024);//2K

//设置临时目录写数据会向临时目录存放一些临时数据。

factory.setRepository(new File(tempFilePath));

ServletFileUpload(可理解为接收客户端请求,解析请求的流对象)

//创建一个文件上传的处理器对象

ServletFileUpload upload = new ServletFileUpload(factory);

//设置允许上传的文件的最大尺寸

upload.setSizeMax(4*1024*1024);//4M

//items存放都是FileItem对象

List items = upload.parseRequest(req);

遍历items,根据FileItem对象的 isFormField()方法判断类型,采取相应操作。

2request获得请求参数

浏览器请求方法最常用的两种:GET POST。两种方式都可以向服务器发送请求参数。

²  GET请求方式

1)在浏览器地址栏直接输入URLname=value&name=value形式。

2)超链接点我

3)表单

或不写method属性,默认即为GET方式。此时提交到服务器时,在浏览器的URL和请求首行的URI中,会包含?name=value&name=value…形式的表单数据信息。

²  POST请求方式

仅在表单中,通过method设置。

POST方式提交时,所有表单数据的值在请求正文中以name=value&name=value…形式发送给服务器。

²  GET请求和POST请求区别?    (面试题!)

1)GET请求参数的值会在浏览器地址栏显示,不安全POST方式请求参数的值都在请求正文中,不会显示在浏览器上,相对安全。

2)GET请求参数在地址栏中有长度限制(1k);POST请求参数在请求正文中,没有长度限制。

3)GET方式所有请求参数数据都在请求首行的URI中,服务器只能通过new String(param.getBytes(“ISO-8859-1”, “utf-8”))形式设置参数编码POST方式既可以通过上述方式,也可通过reqeust.setCharacterEncoding(“UTF-8”)方式设置参数编码。此方法用于设置请求正文的编码(必须在getPrameter之前设置)

案例: 演示超链接

3验证码(动态生成图片)

最初的网站是没有验证码的,黑客就利用程序自动注册大量垃圾用户(僵尸用户)。为了防止自动注册,才使用验证码。

验证码原理:在服务器端产生一个随机的(字母,数字,图片(12306))等序列,然后生成随机的图片,将图片发送给客户端,用户需要识别出图片内容,在注册或登录时,填入验证码的值,连同注册或登录信息一并发送到服务器上,服务器接收后,比对验证码是否一致,如果一致才允许注册或登录操作

即使有验证码,黑客也很快找到了应对技术(OCR: 文字识别软件),对网页上的验证码图片进行自动识别,为了对抗OCR,又在生成的验证码图片上面加入了干扰线。然而目前OCR解决了干扰线。所以,目前验证码并不能完全保证暴力破解或恶意注册。验证码技术还在发展,OCR也在发展,还需要探索更好的算法,去应对这些问题。

案例:如何绘制图片,并将图片发送到浏览器


案例:实现网页验证码功能


总结:以上的验证码还是比较弱,现在出现了很多字符显示的变换和其他类型的验证码,字符显示变换常见的有字体和大小随机变化,显示位置变化,字符变形显示(需要动用三角函数)。还有回答问题的验证码,找图片的验证码等形式。

4Cookie

Cookie,英文含义点心

作用:浏览器访问Web服务器时,服务器在客户端硬盘上存放的信息(服务器给客户的点心),服务器可根据Cookie跟踪客户端信息。

运行机制类比:健身房会员卡相似,第一次报名,健身房发送会员卡,会员卡存储了客户信息。以后客户每次去健身房,先出示会员卡,健身房根据会员卡信息,判断是否允许健身。

Cookie运行机制:客户端首次访问服务器时,服务器先在客户端存放包含该客户相关信息的Cookie,以后客户端每次请求访问服务器时,都会在HTTP请求数据中包含Cookie,服务器解析HTTP请求中的Cookie,获得客户信息。

Cookie运行机制由HTTP协议规定,大多数浏览器和服务器都支持Cookie服务器为了支持Cookie,需要具备可以在HTTP响应结果中添加Cookie数据,解析HTTP请求中的Cookie数据;浏览器为了支持Cookie,需要具备可以解析HTTP响应结果中的Cookie数据,将Cookie数据保存在本地硬盘,读取本地硬盘上的Cookie数据,将其添加到HTTP请求中。

Servlet访问Cookie的实现:

²  Servlet无需和HTTP请求或响应中的原始Cookie数据关联,Servlet容器提供了为Servlet访问Cookie的方法。Cookiejavax.servlet.http.Cookie类表示,每个Cookie对象包含一个namevalue

²  Servlet中创建Cookie对象,并添加到HTTP响应头中

Cookiecookie = new Cookie(“username”, “tom”);

response.addCookie(cookie);

 

²  Servlet中获取客户端发来的Cookie对象

//返回客户端发来所有Cookie,如果请求中没有,返回null

Cookie []cookies = request.getCookies();

遍历,通过Cookie对象getName,getValue获取Cookie数据

²  设置Cookie对象的有效期

Servlet向客户端写数据Cookie时,可以设置Cookie对象的有效期(Cookie默认的有效期为-1)。告诉浏览器如何处理Cookie。使用方法

cookie.setMaxAge(int  expiry)

参数expiry

²  大于0:就告诉浏览器再客户端硬盘上保存Cookie的时间为expiry秒。

²  等于0:告诉浏览器删除当前Cookie

²  小于0:告诉浏览器不要把Cookie保存到客户端硬盘上,仅仅存在于当前浏览器进程中,浏览器关闭,Cookie对象也消失。

 

Servlet可通过getMaxAge()获取Cookie的有效期。 

 

      案例:演示Cookie

      1- 当设置的cookiename相同,value不相同时,后设置的value会覆盖以前。

     2- 一个浏览器可以保存一个服务器的多个cookie

     3- 不同的浏览器,只能访问自己的cookie。

     4-  Servlet只有一个对象,那么该类中的属性,被所有的客户端共享。

     

      案例:演示对Cookie的修改和删除

     

了解以下内容即可:

JavaWeb学习笔记(四)之Servlet_第1张图片

如图:浏览器访问app1应用时,保存了一个Cookie。当再次访问app1, app2, app3应用中的web组件时,浏览器是否会把Cookie添加到HTTP请求中,从而让AServletBServletCServlet组件读取Cookie呢?

app1应用中的组件默认情况下出于安全考虑,只有app1应用中的Web组件能读取该Cookie。如果希望app2, app3能共享Cookie。可在app1AServlet组件发送Cookie时,使用setPath(String path)setDomain(String domain)方法,改变Cookie的共享范围

l  app1app2共享Cookie(同一个tomcat服务器)

Cookie cookie = new Cookie(“username”, “Tom”);

///表示tomcat服务器根目录webapps,所有的web应用共享该cookie

cookie.setPath(“/”); 

resp.addCookie(cookie);

l  只能让app2访问(同一个tomcat服务器)

Cookie cookie = new Cookie(“username”, “Tom”);

///表示tomcat服务器根目录webapps,只有app2能访问该cookieapp1也无法访问该Cookie

cookie.setPath(“/app2/”); 

resp.addCookie(cookie);

l  只能让app1应用中/sub子目录下的web组件访问

Cookie cookie = new Cookie(“username”, “Tom”);

//只有app1下的sub子目录中组件可访问该cookie.

cookie.setPath(“/app1/sub/”); 

resp.addCookie(cookie);

l  让其他Tomcat中所有web应用都能访问

Cookie cookie = new Cookie(“username”, “Tom”);

//必须以.  开头

cookie.setDomain(“.cat.com”); 

resp.addCookie(cookie);

l  仅仅让其他tomcat服务器的指定web应用能访问

Cookie cookie = new Cookie(“username”, “Tom”);

//仅仅让tomcat服务器Bapp3应用访问cookie.

cookie.setDomain(“.cat.com”);

cookie.setPath(“/app3/”);

resp.addCookie(cookie);

 

注意:服务器对客户端进行读写Cookie操作,会给客户端带来安全隐患。服务器可能会发送包含恶意代码的Cookie数据;服务器可能会依据客户单Cookie信息窃取用户的保密信息。处于安全考虑,多数浏览器可以设置是否启动Cookie。工具->Internet选项->隐私->高级。

5访问Web应用的工作目录(了解)

每个Web应用都有一个工作目录。Servlet容器会把Web应用的相关临时文件存放到该目录。默认的工作目录为work\Catalina\localhost\web名称。

Tomcat还允许配置Web应用的元素,用workDir属性配置web应用的工作目录。

Servlet容器及web应用中的Servlet都可以访问工作目录。当Servlet容器初始化一个Web应用时,会向刚创建的ServletContext对象中设置一个名为”javax.servlet.context.tempdir”的属性(预先设置很多属性),属性值为java.io.File对象,代表当前web应用的工作目录。

可通过以下方式获得:

File  workDir = (File)context.getAttribute(“javax.servlet.context.tempdir”);

案例演示:


 

6请求转发和包含

ServletServlet之间无法直接调用,因为Servlet对象由Servlet容器创建并调用,在一个Servlet中无法获得另一个Servlet对象的引用。

实际使用中,Web应用响应客户端的一个请求时,可能需要多个Web组件共同协作,才能生成响应结果。此时,就需要一直机制,能够协调web组件之间协作。Servlet规范中为Web组件之间的协作提供了两种途径。请求转发和包含

²  请求转发(源组件一般为Servlet,目标组件一般为JSP页面)
Servlet(源组件)先对客户端请求做一些预处理操作,然后把请求转发给其他Web组件(目标组件)来完成包括生成响应结果在内的后续操作。

²  包含(经常使用在JSP,多个页面的合并,提高复用)
Servlet(源组件)把其他Web组件(目标组件)生成的响应结果包含到自身的响应结果中。

请求转发和包含的共同点:

²  源组件和目标组件处理的都是同一个客户请求,源组件和目标组件共享同一个ServletRequestServletResponse对象。

²  目标组件可以为Servlet, JSP, HTML

²  都依赖javax.servlet.RequestDispatcher接口。该接口表示请求分发器。Servlet中可通过两种方式得到RequestDispatcher对象

Ø  调用ServletContextgetRequestDispatcher(String path)方法

参数path必须以/   开头,表示当前Web应用的URL入口。此种形式也叫绝对路径

Ø  调用ServletRequestgetRequestDispatcher(String path)方法

参数path既可以以/  开头(绝对路径),也可以不以/   开头(相对路径)

            注意:

   /  开头的可称为绝对路径,表示当前Web应用的URL入口。  不以  / 开头为相对路径,表示相对于当前源Servlet组件的URL路径(web.xml中配置的url-pattern)。

 

案例:  请求转发案例及细节分析:

请求转发使用ReqeustDispatcherforward(RequestServlet  req, ResponseServlet res)转发给目标组件。     


请求转发注意细节:

a)dispatcher.forward()方法处理流程,首先清空存放响应正文数据的缓冲区,其次如果目标组件为ServletJSP,就调用它的service方法,并把该方法响应的结果发送到客户端;如果目标组件为HTML文档,就读取文档中的数据,将其发送到客户单。

b)根据a所言,forward方法会清空源组件存放响应正文的缓冲区,因此Servlet源组件生成的响应结果不会被发送到客户端,只有目标组件生成的响应结果才会被发送到客户端。

c)如果源组件在请求转发之前,已经提交了响应结果(比如调用ServletResponseflushBuffer()方法,或者关闭了输出流out对象),会将forward之前发送给客户端的信息成功发送,但forward方法会抛出IllegalStateException,请求转发将不成功。因此为了避免该异常,不应该在源组件中提交响应结果。(演示)

d)根据案例,forward方法之后的代码也会被执行。只是源组件生成的响应结果不会被发送到客户端。

案例:  包含案例及细节分析:

包含使用ReqeustDispatcherinclude(RequestServlet  req, ResponseServlet res)包含目标组件的响应结果。

包含注意细节:

a)MainServlet类将自己的响应结果, header.html内容, GreetServlet生成的响应正文,以及footer.html内容都包含到自己的响应结果中

b)包含的如果为ServletJSP,就调用相应的service方法,把该方法产生的响应正文添加到源组件的响应结果中;如果目标组件为HTML文档,就直接把文档的内容添加到源组件的响应结果中

c)include方法之后的代码继续执行

d)目标组件中对响应状态码或者响应头的修改都会被忽略。

 

面试题:请求转发和包含的区别?

  请求转发,web容器会清空response数据,且转发后,源组件无法写入数据。包含,web容器会把包含的目标组件数据和源组件数据一起响应给客户端。

7请求范围共享

以上案例,用到请求范围内的共享。使用ServletRequest接口中getAttributesetAttribute方法。只要Web组件共享同一个ServletRequest对象(请求转发 和包含都共享请求和响应对象),就可以共享请求范围内的共享数据。

ServletRequestServletResponse对象生命周期:Servlet容器每次接受到一个客户请求,后会先创建这两个对象,并将这两个对象作为参数传给响应的Servletservice方法。当容器把本次响应结果返回给客户后,这两个对象生命周期结束。

8重定向

HTTP协议规定了重定向机制。运行流程

1)浏览器输入URL,访问服务器端的组件

2)服务器端组件返回一个状态码为302及响应头Location(Location的值可能是同一个Web服务器上另一个Web组件的URL,也可能是不在同一个Web服务器上的Web组件的URL),该响应结果是告诉浏览器再请求访问另一个web组件。

3)浏览器接收到响应结果,再立即自动请求访问另一个Web组件

4)浏览器接收另一个Web组件的响应结果

案例:重定向案例及细节分析

重定向使用HttpServletResponse接口中的sendRedirect(String location)方法实现。(注意,sendRedirect方法不在ServletResponse接口中,因为重定向机制是HTTP协议规定的)


重定向细节分析:

。客户端接收的是目标Web组件的响应结果。

²  源组件在重定向之前,提交了响应结果(flushBufferclose),sendRedirect()方法会抛出IllegalStateException,为了避免该异常,不应该在源组件中提交响应结果。

²  response.sendRedirect(String  path)方法之后的代码也会被执行

²  源组件和目标组件是浏览器两次请求,所以不共享ServletRequest对象,因此不共享请求范围内的共享数据。

²  response.sendRedirect(String  location)方法中的location参数,如果以/开头,表示的是当前服务器根路径的URL(webapps)。如果以http:开头,可以是Internet上的任意一个有效的网页。

²  可用response.setStatus(302); response.setHeader("Location""http://www.163.cn")两句代替sendRedirect方法²  Servlet源组件生成的响应结果不会被发送到客户端

请求转发和重定向区别?

  1请求重定向地址栏有变化,而转发无变化

  2请求重定向客户端向服务器发送两次请求,转发发送一次请求

 3重定向不共享ServletRequest对象,因此不共享请求范围内的共享数据

9线程安全问题

同一个servlet-name对应的servlet-class为同一个实例。即servlet对象是单例的。此时多个客户端冰法访问同一个servlet-name对应的对象,则Servlet容器为了保证同时响应多个客户的访问请求,通常为每个请求分配一个线程,这些线程并发执行同一个Servlet对象的service方法。当多个线程并发执行同一个Servlet对象时,可能会导致线程安全问题。

案例1


案例2


 

两个浏览器同时访问。以上会出现并发问题。

既然如此,如何解决并发安全问题?

²  根据实际应用需求,合理决定在Servlet中定义的变量的作用域。变量是实例变量还是局部变量,由实际应用需求决定。

²  对于多线程并发方法共享数据而导致的安全问题,使用Java同步机制对线程进行同步。

 

对以上案例进行修改:

l  合理决定Servlet中定义变量的作用域类型

局部变量:在方法中定义,每个线程调用service方法,都拥有一份自己的局部变量,方法结束,生命周期结束。

实例变量:类的每一个实例都拥有一份自己的实例变量。多个线程同时执行一个Servlet对象的方法,则该方法中访问实例变量,此时这些线程访问的是同一个实例中的同一个实例变量。

案例修改:上例只需将实例变量改为局部变量即可。

l  使用Java线程同步机制对多线程同步

Java同步机制确保在任意一时刻,只允许一个工作线程执行同步代码块中代码。只有当一个线程退出同步代码块时,其他工作线程才允许执行同步代码块。

 

l  补充:JavaWeb中路径

1)ServletContext的两个方法:    
不管是否以/开头  都表示当前web应用URL
getResourceAsStream(“/WEB-INF/classes/web.xml”)
getResourceAsStream(“WEB-INF/classes/web.xml”)
getRealPath(“/WEB-INF/classes/web.xml”)
getRealPath(“WEB-INF/classes/web.xml”)

2)Class获取资源
可以以/开头也可以不以/开头
//当前目录为classes
InputStream is2 = Test.class.getResourceAsStream("/web.xml");
//相对目录为当前类的.class文件所在目录。
InputStream is2 = Test.class.getResourceAsStream("web.xml");

3)ClassLoader获取资源
可以以/开头,也可以不以/开头,都相对当前classes目录。
Test.class.getClassLoader().getResourceAsStream("/web.xml");
Test.class.getClassLoader().getResourceAsStream("web.xml");

4)url-pattern
必须用   /   开头,表示当前web应用目录。提供外部访问入口

5)请求转发和包含路径
context.getRequestDispatcher(“/xxServlet”);此种形式必须以/开头,表示当前Web应用。
以下两种形式,可以/开头,也可以不用/开头。
/开头,表示相对当前Web应用。
request.getRequestDispatcher(“/xxservlet”)
不以/开头,表示相对源组件的url-pattern当前位置。比如访问的是http://ip:port/test/demo01/myservlet此时xxServlet相当于寻找http://ip:port/test/demo01/xxservlet
request.getRequestDispatcher(“xxservlet”)

6)重定向
/开头,表示当前请求主机的webapps目录,test表示Web应用名。 如果不以/开头,表示相对源组件的url-pattern当前目录,很少用。
response.sendRedirect(“/test/testservlet”)
通用方式获得当前web应用入口:context.getContextPath()
除了以上方式, 重定向可以直接给出http://形式访问

7)form表单的action属性
/开头,表示当前请求主机的webapps目录下testweb应用。

不以/开头,表示相对当前页面的路径。即当前显示页面在服务器上的路径。如果当前页面的路径为http://ip:port/test/page/a.html。则此时提交表单,请求的是http://ip:port/test/page/hello.html.

8)超链接图片
/开头,表示访问主机下webappstest的资源。
/test/testservlet”>link
不以/开头,表示相对当前页面的路径。和form相同

总结:涉及到浏览器访问服务器时,建议以   /  开头(form表单,超链接,重定向,图片)表示相对当前主机的根URL  涉及到服务器内部路径相关,如(重定向,包含,url-pattern, context路径,class),也建议使用   /  开头,表示当前web应用。对于classloader读取资源,建议不用/

总之:路径以/开头,请求转发不加web应用名,其他全部要加。

l  补充:JavaWeb编码

1)在地址栏中直接输入http://localhost:8080/test/hello?username=张三。
请求数据的编码由浏览器决定。不同浏览器使用的编码也不相同。IE默认为GB2312Chrome, FireFox使用UTF-8。实际中很少用。

2)从一个静态页面中发出请求信息
一般访问网站,首页大多为html文件。浏览器得到首页后,从首页上通过提交表单数据或超链接,再向服务器发送数据。此时,首页本身的编码由服务器指定,而用户通过首页输入数据的编码也是由页面本身编码决定的。
静态页面可使用meta标签,告诉浏览器页面编码。

案例演示:

3)GET请求和POST请求编码解码问题

4)URL编码和解码
页面数据不管以GETPOST方式传输,提交的数据都是以name=value&name=value…格式进行传输。键值之间以=分割,键值与键值之间以&分割,如果此时value值中包含=&等特殊字符,则会造成服务器端解析错误。因此必须将引起歧义或特殊的字符进行转义,即对其编码。

POST方式提交时,表单数据在请求正文中,默认自动对其进行URL编码。需要手动进行URL编码的,只有GET,超链接。

URL编码的规则:

  • 字母数字字符 "a "z""A "Z "0 "9保持不变。
  • 特殊字符 ".""-""* "_保持不变。
  • 空格字符 " 转换为一个加号 "+"
  • 所有其他字符都是不安全的,因此首先使用一些编码机制将它们转换为一个或多个字节。然后每个字节+256用一个包含 个字符的字符串 "%xy表示,其中 xy 为该字节的两位十六进制表示形式。推荐的编码机制是 UTF-8。但是,出于兼容性考虑,如果未指定一种编码,则使用相应平台的默认编码。

浏览器端对传输数据进行URL编码,服务器自动识别数据是否使用了URL编码,如果使用了,服务器会自动把数据进行URL解码。可通过如下方式修改服务器默认的URL解码方式 :%tomcat%\conf\server.xml 中找到当前连接器元素 Connector配置URIEncoding 属性

 

注意:对于IE,如果你勾选了高级设置"总是以UTF-8发送Url",那么Url中的路径部分的中文会使用UTF-8进行Url编码之后发送给服务端,而查询参数中的中文部分使用系统默认字符集进行Url编码。

 

后面讲解如何对GET,超链接进行URL编码。

 

 

你可能感兴趣的:(JavaWeb)