Files
webapi-tutorial/docs/urlpattern.md
2024-06-19 17:28:28 +08:00

548 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# URL Pattern API
## 简介
URL Pattern API 基于正则表达式和通配符,对 URL 进行匹配和解析。
它提供一个构造函数`URLPattern()`,用于新建一个 URL 模式实例。
```javascript
const pattern = new URLPattern(input);
```
有了模式实例,就可以知道某个 URL 是否符合该模式。
```javascript
const pattern = new URLPattern({ pathname: "/books" });
console.log(pattern.test("https://example.com/books")); // true
```
上面示例中,模式实例是 包含`/books`路径的 URL实例方法`test()`用来检测指定网址是否符合该模式,结果为`true`
URL Pattern 支持多种协议,不仅是 HTTP 协议。
```javascript
const pattern = new URLPattern("data\\:foo*");
```
上面示例中URL Pattern 新建了一个 Data 协议的模式。
## 构造函数 URLPattern()
### 基本用法
构造函数`URLPattern()`用于新建一个 URL 模式实例。
```javascript
const pattern = new URLPattern(input);
```
该构造函数的参数`input`是一个模式字符串或者模式对象。
```javascript
new URLPattern("https://example.com/books/:id")
// {
// hasRegExpGroups: false,
// hash: "*",
// hostname: "example.com",
// password: "*",
// pathname: "/books/:id",
// port: "",
// protocol: "https",
// search: "*",
// username: "*",
// ...
// }
```
上面示例中,参数`https://example.com/books/:id`就是一个模式字符串,执行后返回一个 URLPattern 实例对象,包含模式的各个组成部分。
参数`input`也可以写成一个对象,用属性指定模式 URL 的每个部分。也就是说,模式对象可以有以下属性。
- protocol
- username
- password
- hostname
- port
- pathname
- search
- hash
- baseURL
上面的示例,如果参数改成模式对象,就是下面这样。
```javascript
new URLPattern({
protocol: 'https',
hostname: 'example.com',
pathname: '/books/:id',
})
```
模式字符串或者模式对象之中,没有定义的部分,默认为`*`,表示所有可能的字符,包括零字符的情况。
`URLPattern()`正常情况下将返回一个 URLPattern 实例对象,但是遇到参数无效或语法不正确,则会报错。
```javascript
new URLPattern(123) // 报错
```
上面示例中,参数`123`不是一个有效的 URL 模式,就报错了。
需要注意的是,如果模式字符串为相对路径,那么`URLPattern()`还需要第二个参数,用来指定基准 URL。
```javascript
new URLPattern(input, baseURL)
```
上面代码中,第二个参数`baseURL`就是基准 URL。
```javascript
new URLPattern('/books/:id') // 报错
new URLPattern('/books/:id', 'https://example.com') // 正确
```
上面示例中,第一个参数`/books/:id`是一个相对路径,这时就需要第二个参数`https://example.com`,用来指定基准 URL否则报错。
但是,如果参数为模式对象,则可以只指定 URL 模式的某个部分。
```javascript
new URLPattern({
pathname: '/books/:id'
}) // 正确
```
上面示例中,参数是一个模式对象,那么参数允许只指定 URL 的部分模式。
模式对象里面,也可以指定基准 URL。
```javascript
let pattern4 = new URLPattern({
pathname: "/books/:id",
baseURL: "https://example.com",
});
```
基准 URL 必须是合法的 URL不能包含模式。
注意,如果用了模式对象,就不能使用基准 URL 作为第二个参数,这样会报错。
```javascript
new URLPattern({ pathname: "/foo/bar" }, "https://example.com") // 报错
new URLPattern({ pathname: "/foo/bar" }, "https://example.com/baz") // 报错
```
上面示例中,同时使用了模式对象和第二个参数,结果就报错了。
`URLpattern()`还可以加入配置对象参数,用于定制匹配行为。
```javascript
new URLPattern(input, options)
new URLPattern(input, baseURL, options)
```
上面代码中,参数`options`就是一个配置对象。
目前,这个配置对象`options`只有`ignoreCase`一个属性,如果设为`true`,将不区分大小写,默认值为`false`,表示区分大小写。
```javascript
new URLPattern(input, {
ignoreCase: false // 默认值,区分大小写
})
```
请看下面的例子。
```javascript
const pattern = new URLPattern("https://example.com/2022/feb/*");
pattern.test("https://example.com/2022/feb/xc44rsz") // true
pattern.test("https://example.com/2022/Feb/xc44rsz") // false
```
上面示例,默认匹配时,会区分`feb``Feb`
我们可以用`ignoreCase`将其关闭。
```javascript
const pattern = new URLPattern(
"https://example.com/2022/feb/*",
{ ignoreCase: true, }
);
pattern.test("https://example.com/2022/feb/xc44rsz") // true
pattern.test("https://example.com/2022/Feb/xc44rsz") // true
```
### 模式写法
模式字符串基本上采用正则表达式的写法,但是不是所有的正则语法都支持,比如先行断言和后行断言就不支持。
1普通字符
如果都是普通字符,就表示原样匹配。
```javascript
const p = new URLPattern('https://example.com/abc');
```
上面代码就表示确切匹配路径`https://example.com/abc`
```javascript
p.test('https://example.com') // false
p.test('https://example.com/a') //false
p.test('https://example.com/abc') // true
p.test('https://example.com/abcd') //false
p.test('https://example.com/abc/') //false
p.test('https://example.com/abc?123') //true
```
上面示例中URL 必须严格匹配路径`https://example.com/abc`,即使尾部多一个斜杠都不行,但是加上查询字符串是可以的。
2`?`
量词字符`?`表示前面的字符串可以出现0次或1次即该部分可选。
```javascript
let pattern = new URLPattern({
protocol: "http{s}?",
});
```
上面示例中,`{s}?`表示字符组`s`可以出现0次或1次。
`?`不包括路径的分隔符`/`
```javascript
const pattern = new URLPattern("/books/:id?", "https://example.com");
pattern.test("https://example.com/books/123") // true
pattern.test("https://example.com/books") // true
pattern.test("https://example.com/books/") // false
pattern.test("https://example.com/books/123/456") // false
pattern.test("https://example.com/books/123/456/789") // false
pattern.test("https://example.com/books/123/456/") // false
```
上面示例中,`?`不能匹配网址结尾的斜杠。
如果一定要匹配,可以把结尾的斜杠放在`{}`里面。
```javascript
const pattern = new URLPattern({ pathname: "/product{/}?" });
pattern.test({ pathname: "/product" }) // true
pattern.test({ pathname: "/product/" }) // true
```
上面示例中,不管网址有没有结尾的斜杠,`{/}?`都会成功匹配。
3`+`
量词字符`+`表示前面的字符串出现1次或多次。
```javascript
const pattern = new URLPattern({
pathname: "/books/(\\d+)",
})
```
上面示例中,`\\d+`表示1个或多个数字其中的`\d`是一个内置的字符类表示0-9的数字因为放在双引号里面所以反斜杠前面还要再加一个反斜杠进行转义。
`+`可以包括`/`分隔的路径的多个部分,但不包括路径结尾的斜杠。
```javascript
const pattern = new URLPattern("/books/:id+", "https://example.com");
pattern.test("https://example.com/books/123") // true
pattern.test("https://example.com/books") // false
pattern.test("https://example.com/books/") // false
pattern.test("https://example.com/books/123/456") // true
pattern.test("https://example.com/books/123/456/789") // true
pattern.test("https://example.com/books/123/456/") // false
```
4`*`
量词字符`*`表示出现零次或多次。
```javascript
const pattern = new URLPattern('https://example.com/{abc}*');
pattern.test('https://example.com') // true
pattern.test('https://example.com/') // true
pattern.test('https://example.com/abc') // true
pattern.test('https://example.com/abc/') // false
pattern.test('https://example.com/ab') // false
pattern.test('https://example.com/abcabc') // true
pattern.test('https://example.com/abc/abc/abc') // false
```
上面示例中,`{abc}*`表示`abc`出现零次或多次,也不包括路径分隔符`/`
如果`*`前面没有任何字符,就表示所有字符,包括零字符的情况,也包括分隔符`/`
```javascript
let pattern = new URLPattern({
search: "*",
hash: "*",
});
```
上面示例中,`*`表示匹配所有字符,包括零字符。
下面是另一个例子。
```javascript
const pattern = new URLPattern("/*.png", "https://example.com");
pattern.test("https://example.com/image.png") // true
pattern.test("https://example.com/image.png/123") // false
pattern.test("https://example.com/folder/image.png") // true
pattern.test("https://example.com/.png") // true
```
`*`匹配的部分可以从对应部分的数字属性上获取。
```javascript
const pattern = new URLPattern({
hostname: "example.com",
pathname: "/foo/*"
});
const result = pattern.exec("/foo/bar", "https://example.com/baz");
result.pathname.input // '/foo/bar'
result.pathname.groups[0] // 'bar'
```
上面示例中,`*`的匹配结果可以从`pathname.groups[0]`获取。
```javascript
const pattern = new URLPattern({ hostname: "*.example.com" });
const result = pattern.exec({ hostname: "cdn.example.com" });
result.hostname.groups[0] // 'cdn'
result.hostname.input // 'cdn.example.com'
```
上面示例中,`*`的匹配结果可以从`hostname.groups[0]`获取。
5`{}`
特殊字符`{}`用来定义量词`?``+``+`的生效范围。
如果`{}`后面没有量词,那就跟没有使用的效果一样。
```javascript
const pattern = new URLPattern('https://example.com/{abc}');
pattern.test('https://example.com/') // false
pattern.test('https://example.com/abc') // true
```
6`()`
特殊字符`()`用来定义一个组匹配,匹配结果可以按照出现顺序的编号,从`pathname.groups`对象上获取。
```javascript
const pattern = new URLPattern("/books/(\\d+)", "https://example.com");
pattern.exec("https://example.com/books/123").pathname.groups
// { '0': '123' }
```
上面示例中,`(\\d+)`是一个组匹配,因为它是第一个组匹配,所以匹配结果放在`pathname.groups`的属性`0`
7`|`
特殊字符`|`表示左右两侧的字符,都可以出现,即表示逻辑`OR`
```javascript
let pattern = new URLPattern({
port: "(80|443)",
});
```
上面示例中,`(80|443)`表示80或者443都可以。
8`:`
特殊字符`:`用来定义一个具名组匹配,后面跟着变量名。
```javascript
let pattern = new URLPattern({
pathname: "/:path",
});
```
上面示例中,`/:path`表示斜杠后面的部分,都被捕捉放入变量`path`,可以从匹配结果的`pathname.groups`上的对应属性获取。
```javascript
const pattern = new URLPattern({ pathname: "/books/:id" });
pattern.exec("https://example.com/books/123").pathname.groups
// { id: '123' }
```
上面示例中,`pathname.groups`返回一个对象,该对象的属性就是所有捕捉成功的组变量,上例是`id`
下面是另一个例子。
```javascript
const pattern = new URLPattern({ pathname: "/:product/:user/:action" });
const result = pattern.exec({ pathname: "/store/wanderview/view" });
result.pathname.groups.product // 'store'
result.pathname.groups.user // 'wanderview'
result.pathname.groups.action // 'view'
result.pathname.input // '/store/wanderview/view'
```
上面示例中,`:product``:user``:action`的匹配结果,都可以从`pathname.groups`的对应属性上获取。
组匹配可以放在模式的前面。
```javascript
const pattern = new URLPattern(
"/books/:id(\\d+)",
"https://example.com"
);
```
上面示例中,组匹配`:id`后面跟着模型定义`\\d+`,模式需要放在括号里面。
**9特殊字符转义**
如果要将特殊字符当作普通字符使用,必须在其前面加入双重反斜杠进行转义。
```javascript
let pattern1 = new URLPattern({
pathname: "/a:b",
});
let pattern2 = new URLPattern({
pathname: "/a\\:b",
});
```
上面示例中,`a:b`表示路径以字符`a`开头,后面的部分都放入变量`b`。而`a\\:b`表示路径本身就是`a:b`就是。
## 实例属性
URLPattern 实例的属性对应`URLPattern()`模式对象参数的各个部分。
```javascript
const pattern = new URLPattern({
hostname: "{*.}?example.com",
});
pattern.hostname // '{*.}?example.com'
pattern.protocol // '*'
pattern.username // '*'
pattern.password // '*'
pattern.port // ""
pattern.pathname // '*'
pattern.search // '*'
pattern.hash // '*'
```
上面示例中,`pattern`是一个实例对象,它的属性与`URLPattern()`的参数对象的属性一致。
注意,`search`不包括开头的`?``hash`不包括开头的`#`,但是`pathname`包括开头的`/`
下面是另一个例子。
```javascript
const pattern = new URLPattern("https://cdn-*.example.com/*.jpg");
pattern.protocol // 'https'
pattern.hostname // 'cdn-*.example.com'
pattern.pathname // '/*.jpg'
pattern.username // ''
pattern.password // ''
pattern.search // ''
pattern.hash // ''
```
## 实例方法
### exec()
实例的`exec()`方法,把模式用于解析参数网址,返回匹配结果。
`exec()`方法的参数与`new URLPattern()`是一致的。它可以是一个 URL 字符串。
```javascript
pattern.exec("https://store.example.com/books/123");
```
如果第一个参数是相对 URL那么需要基准 URL作为第二个参数。
```javascript
pattern.exec("/foo/bar", "https://example.com/baz");
```
`exec()`方法的参数,也可以是一个对象。
```javascript
pattern.exec({
protocol: "https",
hostname: "store.example.com",
pathname: "/books/123",
});
```
如果匹配成功,它返回一个包括匹配结果的对象。如果匹配失败,返回`null`
```javascript
const pattern = new URLPattern("http{s}?://*.example.com/books/:id");
pattern.exec("https://example.com/books/123") // null
```
上面示例中,匹配失败返回`null`
匹配成功返回的对象,有一个`inputs`属性,包含传入`pattern.exec()`的参数数组。其他属性的值也是一个对象,该对象的`input`属性对应传入值,`groups`属性包含各个组匹配。
```javascript
const pattern = new URLPattern("http{s}?://*.example.com/books/:id");
let match = pattern.exec("https://store.example.com/books/123");
match.inputs // ['https://store.example.com/books/123']
match.protocol // { input: "https", groups: {} }
match.username // { input: "", groups: {} }
match.password // { input: "", groups: {} }
match.hostname // { input: "store.example.com", groups: { "0": "store" } }
match.port // { input: "", groups: {} }
match.pathname // { input: "/books/123", groups: { "id": "123" } }
match.search // { input: "", groups: {} }
match.hash // { input: "", groups: {} }
```
### test()
实例的`test()`方法,用来检测参数网址是否符合当前模式。
它的参数跟`URLPattern()`是一样的,可以是模式字符串,也可以是模式对象。
```javascript
const pattern = new URLPattern({
hostname: "example.com",
pathname: "/foo/*"
});
pattern.test({
pathname: "/foo/bar",
baseURL: "https://example.com/baz",
}) // true
pattern.test("/foo/bar", "https://example.com/baz") // true
```
正常情况下,它返回一个布尔值。但是,如果语法不合法,它也会抛错。
```javascript
pattern.test({ pathname: "/foo/bar" }, "https://example.com/baz") // 报错
```