跳转至
内容字体
东观体
上图东观体
OPPO Sans
江城黑体
霞鹜臻楷
代码字体
DejaVu Sans Mono
DejaVu Sans Mono
Google Sans Code
JetBrains Mono
主题切换
返回顶部

乱码问题和路径问题

约 3232 个字 169 行代码 29 张图片 预计阅读时间 13 分钟

乱码问题

在Java基础部分已经提到过字符编码,而所谓的乱码就是编码时使用的字符集和解码时使用的字符集不同,所以解决乱码的唯一方式就是确保编码和解码使用的是同一个编码

在Web开发阶段,有下面几种乱码的情况:

  1. HTML乱码
  2. Tomcat控制台乱码
  3. 请求乱码问题

    1. GET请求乱码
    2. POST方式请求乱码
  4. 响应乱码问题

HTML乱码问题

设置项目文件的字符集要使用一个支持中文的字符集

  1. 查看当前文件的字符集

  2. 查看项目字符集配置,将Global Encoding全局字符集,Project Encoding项目字符集,Properties Files属性配置文件字符集设置为UTF-8

HTML文件的字符集通过<meta charset="UTF-8">来告知浏览器通过什么字符集来解析当前文件,例如下面的文件:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    中文
</body>
</html>

在IDEA中,如果更改了<meta charset="UTF-8">中的UTF-8,那么IDEA会自动更改当前文件的编码

Tomcat控制台乱码问题

在Tomcat10这个版本中,修改tomcat->conf->logging.properties中,所有的UTF-8为GBK即可

修改前:

修改后:

如果遇到使用System.out.prinln()输出乱码,则需要告诉JVM虚拟机以什么字符集进行解码。在编写文件时,IDEA会根据文件的字符集将当前的.java文件编译成对应字符集的.class文件,但是解码时,IDEA并没有告知JVM使用何种字符集进行解码,所以此时需要进行下面的设置:

GET请求乱码问题

GET请求方式乱码原因分析:

GET方式提交参数的方式是将参数放到URL后面,如果使用的不是UTF-8,那么会对参数进行URL编码处理,而HTML中的<meta charset='字符集'/>影响了GET方式提交参数的URL编码,在Tomcat10中,URI编码默认为UTF-8,当GET方式提交的参数URL编码和Tomcat10默认的URI编码不一致时就会出现乱码

GET请求乱码问题解决方式如下:

方式1:设置GET方式提交的编码和Tomcat的URI默认解析编码一致即可 (推荐)

方式2:设置Tomcat10的URI解析字符集和GET请求发送时所使用URL转码时的字符集一致即可,修改conf/server.xmlConnecter添加URIEncoding="GBK"(不推荐)

Note

之所以不推荐这种方式,是因为这种方式取决于Tomcat所运行的系统环境,如果另一台服务器的编码不是GBK,则此时的Tomcat在放到该服务器时就会出现乱码

POST方式请求乱码问题

POST请求方式乱码原因分析:

POST请求将参数放在请求体中进行发送,请求体使用的字符集受到了<meta charset="字符集"/>的影响,而Tomcat10默认使用UTF-8字符集对请求体进行解析,如果请求体的URL转码和Tomcat的请求体解析编码不一致就容易出现乱码

POST请求方式乱码解决方式如下:

方式1:请求时,使用UTF-8字符集提交请求体(推荐)

方式2:后端在获取参数前,设置解析请求体使用的字符集和请求发送时使用的字符集一致(不推荐)

响应乱码问题

响应乱码原因分析:

在Tomcat10中,向响应体中放入的数据默认使用了工程编码为UTF-8,浏览器在接收响应信息时,使用了不同的字符集或者是不支持中文的字符集就会出现乱码

响应乱码解决方式:

方式1:手动设定浏览器对本次响应体解析时使用的字符集(不推荐),因为Edge和Chrome浏览器都没有提供直接的比较方便的入口,不方便

方式2:后端通过设置响应体的字符集和浏览器解析响应体的默认字符集一致(不推荐)

方式3:通过设置Content-Type响应头告诉浏览器以指定的字符集解析响应体(推荐)

路径问题

定位一个资源时,有两种路径可以选择:

  1. 绝对路径
  2. 相对路径

相对路径的特点:

  • 相对路径的规则是: 以当前资源所在的路径为出发点去寻找目标资源
  • 相对路径不以/开头
  • 在file协议下,使用的是磁盘路径
  • 在http协议下,使用的是url路径
  • 相对路径中可以使用./表示当前资源所在路径,可以省略不写
  • 相对路径中可以使用../表示当前资源所在路径的上一层路径,需要时要手动添加

绝对路径的特点:

  • 绝对路径的规则是:使用以一个固定的路径做出出发点去寻找目标资源,和当前资源所在的路径没有关系
  • 绝对路径要以/开头
  • 绝对路径的写法中,不以当前资源的所在路径为出发点,所以不会出现./../
  • 不同的项目和不同的协议下,绝对路径的基础位置可能不同,要通过测试确定
  • 绝对路径的好处就是:无论当前资源位置在哪,寻找目标资源路径的写法都一致

本次要讨论的路径问题就是基于上面两种的路径,常见的路径问题有下面几种:

  1. 前端路径问题
  2. 重定向中的路径问题
  3. 请求转发中的路径问题

前端路径问题

前端路径问题一般出现在HTML文件中访问其他资源时,例如要获取到其他位置的图片、访问其他网页等,下面以访问图片为例

下面是当前目录结构:

相对路径

index.html中引入static/img/image.png文件,代码如下:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>这是index</h1>
<img src="static/img/image.png" />
</body>
</html>

启动Tomcat可以看到网页中正常显示了图片内容,下面分析为什么index.html中的图片会被正常访问到:

访问index.htmlurl为:http://localhost:8080/FrontEnd_war_exploded/index.html,当前资源为index.html,当前资源(index.html)的所在路径为:http://localhost:8080/FrontEnd_war_exploded/。要获取的目标资源url为 : http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png

因为index.html中定义的了:<img src="static/img/logo.png"/>,因为是相对路径,所以寻找方式就是在当前资源所在路径(http://localhost:8080/FrontEnd_war_exploded/)后拼接src属性值:static/img/logo.png,正好是目标资源正常获取的url,即http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png

而根据前面磁盘的路径图构建后的artifacts结构正好可以找到static/img/logo.png文件,所以正常加载图片:

Note

此处展示构建后的artifacts只是为了演示为什么可以正常读取,在实际开发中不可能先构建再写路径,所以需要记住web目录下的内容除了WEB_INFlib以外,其他目录都会处于项目的根路径下,特别注意src目录会被转换为classes中的文件存放到WEB_INF

接下来,在test.html文件中使用同样的static/img/image.png引入图片:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
</head>
<body>
<h1>这是test</h1>
<img src="static/img/image.png"/>
</body>
</html>

启动Tomcat访问test.html可以看到无法正常加载图片,原因如下:

访问test.htmlurl为:http://localhost:8080/FrontEnd_war_exploded/a/b/c/test.html,当前资源为:test.html,当前资源的所在路径为:http://localhost:8080/FrontEnd_war_exploded/a/b/c/,所以寻找方式就是在当前资源所在路径http://localhost:8080/FrontEnd_war_exploded/a/b/c/后拼接src属性值(static/img/logo.png),此时因为a/b/c下不存在static/img/logo.png,所以找不到对应的资源

因为要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png,可以考虑使用../抵消掉a/b/c,此时test.html中的imgsrc就是:<img src="../../../static/img/logo.png"/>。 其中../可以抵消一层路径,正好是目标资源正常获取的urlhttp://localhost:8080/FrontEnd_war_exploded/static/img/logo.png

接着在view1.html中引入static/img/logo.png,但是因为WEB-INF中的内容无法直接访问,此时需要用到请求转发,在view1Servlet中编写请求转发代码:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@WebServlet("/view1Servlet")
public class View1Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("这是view1Servlet");

        RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
        requestDispatcher.forward(req, resp);
    }
}

再向view1.html写入下面的代码:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>view1</title>
</head>
<body>
<h1>这是view1</h1>
<img src="static/img/image.png" />
</body>
</html>

启动Tomcat可以看到正常显示图片,此时就不是直观的看磁盘目录结构了,因为直接看磁盘目录结构可以发现view1.html不可能按照static/img/image.png可以找到图片,实际上是因为请求转发不会替换地址栏的内容,本质上就是通过替换view1Servlet来拼接static/img/image.png,所以就可以正常找到图片

绝对路径

接下来看绝对路径的情况:

绝对路径情况1:web/index.html中引入static/img/logo.png

  • 访问index.htmlurl为:http://localhost:8080/FrontEnd_war_exploded/index.html
  • 绝对路径的基准路径为:http://localhost:8080
  • 要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png
  • index.html中定义的了:<img src="/FrontEnd_war_exploded/static/img/logo.png"/>
  • 寻找方式就是在基准路径(http://localhost:8080)后面拼接src属性值(/FrontEnd_war_exploded/static/img/logo.png),得到的正是目标资源访问的正确路径
HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- 绝对路径写法 -->
    <img src="/FrontEnd_war_exploded/static/img/logo.png">
</body>
</html>

绝对路径情况2:a/b/c/test.html中引入static/img/logo.png

  • 访问test.htmlurl为:http://localhost:8080/FrontEnd_war_exploded/a/b/c/test.html
  • 绝对路径的基准路径为:http://localhost:8080
  • 要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png
  • test.html中定义的了:<img src="/FrontEnd_war_exploded/static/img/logo.png"/>
  • 寻找方式就是在基准路径(http://localhost:8080)后面拼接src属性值(/FrontEnd_war_exploded/static/img/logo.png),得到的正是目标资源访问的正确路径
HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!-- 绝对路径写法 -->
    <img src="/FrontEnd_war_exploded/static/img/logo.png">
</body>
</html>

绝对路径情况3:/WEB-INF/views/view1.html中引入static/img/logo.png

view1.htmlWEB-INF下,需要通过Servlet请求转发获得

Java
1
2
3
4
5
6
7
8
@WebServlet("/view1Servlet")
public class View1Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
        requestDispatcher.forward(req,resp);
    }
}
  • 访问view1.htmlurl为:http://localhost:8080/FrontEnd_war_exploded/view1Servlet
  • 绝对路径的基准路径为:http://localhost:8080
  • 要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png
  • view1.html中定义的了:<img src="/FrontEnd_war_exploded/static/img/logo.png"/>
  • 寻找方式就是在基准路径(http://localhost:8080)后面拼接src属性值(/static/img/logo.png),得到的正是目标资源访问的正确路径
HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<img src="/FrontEnd_war_exploded/static/img/logo.png">
</body>
</html>

使用base标签定义页面相对路径公共前缀

  • base标签定义在head标签中,用于定义相对路径的公共前缀
  • base标签定义的公共前缀只在相对路径上有效,绝对路径中无效
  • 如果相对路径开头有./或者../修饰,则base标签对该路径同样无效

index.htmla/b/c/test.html以及view1Servlet中的路径处理

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--定义相对路径的公共前缀,将相对路径转化成了绝对路径-->
    <base href="/FrontEnd_war_exploded/">
</head>
<body>
    <img src="static/img/logo.png">
</body>
</html>

项目上下文路径变化问题:通过base标签虽然解决了相对路径转绝对路径问题,但是base中定义的是项目的上下文路径,但是项目的上下文路径是可以随意变化的,一旦项目的上下文路径发生变化,所有base标签中的路径都需要改

解决方案如下:将项目的上下文路径进行缺省设置,设置为/,所有的绝对路径中就不必填写项目的上下文了,直接就是/开头即可

重定向中的路径问题

目标:由/x/y/z/servletA重定向到a/b/c/test.html

Java
1
2
3
4
5
6
7
@WebServlet("/x/y/z/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

相对路径

  • 访问ServletAurl为:http://localhost:8080/FrontEnd_war_exploded/x/y/z/servletA
  • 当前资源为:servletA
  • 当前资源的所在路径为:http://localhost:8080/FrontEnd_war_exploded/x/x/z/
  • 要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/a/b/c/test.html
  • ServletA重定向的路径:../../../a/b/c/test.html
  • 寻找方式就是在当前资源所在路径(http://localhost:8080/FrontEnd_war_exploded/x/y/z/)后拼接(../../../a/b/c/test/html),形成(http://localhost:8080/FrontEnd_war_exploded/x/y/z/../../../a/b/c/test/html)每个../抵消一层目录,正好是目标资源正常获取的urlhttp://localhost:8080/FrontEnd_war_exploded/a/b/c/test/html
Java
1
2
3
4
5
6
7
8
@WebServlet("/x/y/z/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 相对路径重定向到test.html
        resp.sendRedirect("../../../a/b/c/test.html");
    }
}

绝对路径

  • 访问ServletAurl为:http://localhost:8080/FrontEnd_war_exploded/x/y/z/servletA

  • 绝对路径的基准路径为:http://localhost:8080

  • 要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/a/b/c/test.html

  • ServletA重定向的路径:/FrontEnd_war_exploded/a/b/c/test.html

  • 寻找方式就是在基准路径(http://localhost:8080)后面拼接(/FrontEnd_war_exploded/a/b/c/test.html),得到(http://localhost:8080/FrontEnd_war_exploded/a/b/c/test.html)正是目标资源访问的正确路径

绝对路径中需要填写项目上下文路径,但是上下文路径是变换的,可以通过ServletContextgetContextPath()获取上下文路径,也可以将项目上下文路径定义为/缺省路径,那么路径中直接以/开头即可

Java
1
2
3
4
5
6
//绝对路径中,要写项目上下文路径
//resp.sendRedirect("/FrontEnd_war_exploded/a/b/c/test.html");
// 通过ServletContext对象动态获取项目上下文路径
//resp.sendRedirect(getServletContext().getContextPath()-"/a/b/c/test.html");
// 缺省项目上下文路径时,直接以/开头即可
resp.sendRedirect("/a/b/c/test.html");

请求转发中的路径问题

目标:由x/y/servletB请求转发到a/b/c/test.html

Java
1
2
3
4
5
6
7
@WebServlet("/x/y/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

相对路径

  • 访问ServletBurl为:http://localhost:8080/FrontEnd_war_exploded/x/y/servletB

  • 当前资源为:servletB

  • 当前资源的所在路径为:http://localhost:8080/FrontEnd_war_exploded/x/y/

  • 要获取的目标资源url为:http://localhost:8080/FrontEnd_war_exploded/a/b/c/test.html

  • ServletA请求转发路径:../../a/b/c/test/html

  • 寻找方式就是在当前资源所在路径(http://localhost:8080/FrontEnd_war_exploded/x/y/)后拼接(../../a/b/c/test/html),形成(http://localhost:8080/FrontEnd_war_exploded/x/y/../../a/b/c/test/html)每个../抵消一层目录,正好是目标资源正常获取的urlhttp://localhost:8080/FrontEnd_war_exploded/a/b/c/test/html

Java
1
2
3
4
5
6
7
8
@WebServlet("/x/y/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("../../a/b/c/test.html");
        requestDispatcher.forward(req,resp);
    }
}

绝对路径

  • 请求转发只能转发到项目内部的资源,其绝对路径无需添加项目上下文路径

  • 请求转发绝对路径的基准路径相当于http://localhost:8080/FrontEnd_war_exploded

  • 在项目上下文路径为缺省值时,也无需改变,直接以/开头即可

Java
1
2
3
4
5
6
7
8
@WebServlet("/x/y/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/a/b/c/test.html");
        requestDispatcher.forward(req,resp);
    }
}

目标资源内相对路径处理

  • 此时需要注意,请求转发是服务器行为,浏览器不知道,地址栏不变化,相当于访问test.html的路径为http://localhost:8080/FrontEnd_war_exploded/x/y/servletB

  • 那么此时test.html资源的所在路径就是http://localhost:8080/FrontEnd_war_exploded/x/y/所以test.html中相对路径要基于该路径编写,如果使用绝对路径则不用考虑

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--
        当前资源路径是     http://localhost:8080/FrontEnd_war_exploded/x/y/servletB
        当前资源所在路径是  http://localhost:8080/FrontEnd_war_exploded/x/y/
        目标资源路径=所在资源路径+src属性值 
        http://localhost:8080/FrontEnd_war_exploded/x/y/../../static/img/logo.png
        http://localhost:8080/FrontEnd_war_exploded/static/img/logo.png
        得到目标路径正是目标资源的访问路径   
    -->
<img src="../../static/img/logo.png">
</body>
</html>