今天收到一个Bug, 一个超级奇怪的人名叫做 Isxxxxa Onxxxna Anton

LinkedIn居然搜得到这个人全名, 果断隐藏了部分o( ̄▽ ̄)o

一个已经老掉牙的模块报错, 错误原因是URL提交参数出错导致后续JSON解析出错

email.jsp?name=Isxxxxa Onxxxna Anton&email=Onxxxxa.Anton@xxxx.com

首先很严重的原因URL参数里面的空格就应该先被Encode掉

然而..很神奇的..

其他一些更是莫名其妙的名字却没有问题…甚至后面那一段怎么看都觉得会引发错误的邮箱字段却一直没有bug出现

借此机会对URL进行一次深入而系统的学习

URL & URI

what is URL1?

A Uniform Resource Locator (URL), commonly informally termed a web address (a term which is not defined identically) is a reference to a web resource that specifies its location on a computer network and a mechanism for retrieving it. A URL is a specific type of Uniform Resource Identifier (URI), although many people use the two terms interchangeably. A URL implies the means to access an indicated resource, which is not true of every URI. URLs occur most commonly to reference web pages (http), but are also used for file transfer (ftp), email (mailto), database access (JDBC), and many other applications.

这里提到了一句”urluri的特殊形式”

so..what is URI?

In information technology, a Uniform Resource Identifier (URI) is a string of characters used to identify a resource. Such identification enables interaction with representations of the resource over a network, typically the World Wide Web, using specific protocols. Schemes specifying a concrete syntax and associated protocols define each URI. The most common form of URI is the Uniform Resource Locator (URL), frequently referred to informally as a web address. More rarely seen in usage is the Uniform Resource Name (URN), which was designed to complement URLs by providing a mechanism for the identification of resources in particular namespaces.

可以理解为URIURL的父类, URI的目的是指向一个资源, 而URL的目的是引用这个资源

借用Chokcoco博客2的一句话:

URI 属于 URL 更低层次的抽象,一种字符串文本标准。

就是说,URI 属于父类,而 URL 属于 URI 的子类。URL 是 URI 的一个子集。

二者的区别在于,URI 表示请求服务器的路径,定义这么一个资源。而 URL 同时说明要如何访问这个资源(http://)。

URI Syntax

URI的原生格式为

    scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

关于各部分的内容:

Parts Comments
scheme 这里包含各种协议名, http, https, ftp等等
协议名大小写不敏感, 但是一般小写简单可观, 同时一些非开放注册的协议可以使用, 例如迅雷的ed2k, 电驴的协议等等
user name and password 一些特殊的协议访问需要携带这些信息
host 主机名
port  
path  
query 虽然称作Query, 但这部分实际就是URL参数, 不同情况下可以使用&或者;进行分割
fragment 片段, 通过在参数后方放一个#进行判断, 这里会放置一个fragment identifier 来访问当前页面的次级内容, 一般这儿就是放HTML元素的ID
   

URI 编码

为什么要对URI进行编码3?

实际上就是为了防止歧义, 无歧义的情况下直接输入完全没有问题, 然而更多时候我们需要对一些特定的字符进行转换

URI 编码标准

2005年1月发布的RFC 3986,强制所有新的URI必须对未保留字符不加以百分号编码;其它字符要先转换为UTF-8字节序列, 然后对其字节值使用百分号编码。此前的URI不受此标准的影响。

URI 文字类型

URI允许接受2类文字:

  1. Reserved Characters – RFC 3986 Reserved Characters (January 2005)

    Encode Required

    ! * ‘ ( ) ; : @ & = + $ , / ? ## [ ]

  2. Unreserved Characters – RFC 3986 Unreserved Characters (January 2005)

    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

    a b c d e f g h i j k l m n o p q r s t u v w x y z

    0 1 2 3 4 5 6 7 8 9 - _ . ~

编码之后的文字

before after
! 21%
# 23%
$ 24%
& 26%
27%
( 28%
) 29%
* %2A
+ %2B
, %2C
/ %2F
: %3A
; %3B
= %3D
? %3F
@ 40%
[ %5B
] %5D

这里解释了为何之前的Email没有出现和空格一样的错误, 因为@. 字符都属于保留字符, 并且在最早期编码标准中就已经投入使用, 因此多数语言的编码实现都没有问题

空格?

application/x-www-form-urlencoded类型编辑 当HTML表单中的数据被提交时,表单的域名与值被编码并通过HTTPGET或者POST方法甚至更古远的email[2]把请求发送给服务器。

这里的编码方法采用了一个非常早期的通用的URI百分号编码方法,并且有很多小的修改如新行规范化以及把空格符的编码"%20"替换为"+" . 按这套方法编码的数据的MIME类型是application/x-www-form-urlencoded, 当前仍用于(虽然非常过时了)HTML与XForms规范中. 此外,CGI规范包括了web服务器如何解码这类数据、利用这类数据的内容。

关于编程习惯

其实看到这里, 一开始的问题就已经有解决方案了, 就是将那个奇怪的人名中的空格进行百分号编码, 然后提交到服务器或者其他地方直接使用即可

但是这只是一个temp solution, 出错的页面迟早要进行redesign。 不过为了保证程序后期的健壮性, 依然建议URL中不要放置URI 编码标准中无法接受的字符(即除去上述两类字符之外的字符)

同时将空格转为下划线也是个不错的选择

参考文献