摘要:从一次"面试官追问URL和URI的区别"的尴尬经历出发,深度剖析统一资源标识符的核心概念。通过URL、URI、URN的关系图解、以及RESTful API设计中的URI最佳实践,揭秘为什么URL是URI的子集、URN为什么很少用、以及Spring MVC的@RequestMapping为什么叫URI不叫URL。配合真实案例展示URL的组成部分,给出Web开发中的命名规范。
💥 翻车现场
周四下午,哈吉米参加技术面试。
面试官:"聊聊HTTP吧,URL你应该很熟悉?"
哈吉米:"嗯,URL就是网址,比如 https://www.example.com/api/user。"
面试官:"那URI呢?URI和URL有什么区别?"
哈吉米:"呃……URI……好像也是网址?"
面试官:"URN听说过吗?"
哈吉米:"……"(冷汗)
面试官:"那你说说,Spring的@RequestMapping里面是URL还是URI?"
哈吉米:"……应该是URL吧?"
面试官(摇头):"是URI。"
晚上,哈吉米找南北绿豆和阿西噶阿西复盘。
哈吉米:"我被问懵了,URL和URI到底有啥区别?"
南北绿豆:"这是个经典问题,很多人搞不清楚。"
阿西噶阿西:"来,我给你讲清楚。"
🤔 URI、URL、URN的关系
核心概念
阿西噶阿西在白板上画了一个图。
URI(Uniform Resource Identifier):统一资源标识符
├─ URL(Uniform Resource Locator):统一资源定位符
└─ URN(Uniform Resource Name):统一资源名称
关系:
URI = URL + URN
URL ⊂ URI
URN ⊂ URI
图解:
┌─────────────────────────────────────┐
│ URI(统一资源标识符) │
│ │
│ ┌──────────────┐ ┌──────────────┐│
│ │ URL │ │ URN ││
│ │(资源位置) │ │(资源名称) ││
│ └──────────────┘ └──────────────┘│
└─────────────────────────────────────┘
示例:
URI:所有标识资源的字符串
URL:告诉你资源在哪(位置)
URN:告诉你资源叫什么(名称)
南北绿豆:"简单说:URI是总称,URL和URN是两种具体形式。"
🎯 URL:统一资源定位符
定义
URL:通过位置来标识资源。
示例:
https://www.example.com:8080/api/user/123?name=alice#profile
分解:
协议:https
域名:www.example.com
端口:8080
路径:/api/user/123
查询参数:name=alice
锚点:profile
URL的组成部分
标准格式:
scheme://host:port/path?query#fragment
详解:
┌─────────────────────────────────────────────────────────┐
│ https://user:pass@www.example.com:8080/api/user?id=123#top │
│ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ │
│ 协议 用户 密码 主机 端口 路径 查询 锚点│
└─────────────────────────────────────────────────────────┘
各部分说明:
| 部分 | 必须 | 说明 | 示例 |
|---|---|---|---|
| scheme | ✅ | 协议 | http、https、ftp、file |
| user:pass | ❌ | 用户认证 | admin:123456 |
| host | ✅ | 主机(域名或IP) | www.example.com、192.168.1.100 |
| port | ❌ | 端口(默认:http=80, https=443) | 8080 |
| path | ❌ | 路径 | /api/user/123 |
| query | ❌ | 查询参数 | id=123&name=alice |
| fragment | ❌ | 锚点(不发送到服务器) | #top |
URL示例
1. https://www.baidu.com
- 协议:https
- 主机:www.baidu.com
- 端口:443(默认)
- 路径:/(默认)
2. http://localhost:8080/api/user/123
- 协议:http
- 主机:localhost
- 端口:8080
- 路径:/api/user/123
3. ftp://ftp.example.com/files/data.zip
- 协议:ftp
- 主机:ftp.example.com
- 路径:/files/data.zip
4. file:///C:/Users/ajocer/Desktop/test.txt
- 协议:file
- 路径:C:/Users/ajocer/Desktop/test.txt
哈吉米:"所以URL就是告诉你资源在哪里,通过位置找到资源。"
🎯 URN:统一资源名称
定义
URN:通过名称来标识资源,与位置无关。
示例:
URN格式:
urn:namespace:specific-string
示例1:ISBN(国际标准书号)
urn:isbn:978-7-115-54326-9
示例2:UUID
urn:uuid:550e8400-e29b-41d4-a716-446655440000
示例3:Java包名(类似URN)
com.ajocer.service.UserService
URL vs URN
对比:
URL(位置):
https://www.example.com/books/java-book.pdf
→ 告诉你书在example.com服务器的/books/java-book.pdf
URN(名称):
urn:isbn:978-7-115-54326-9
→ 告诉你书的唯一编号,但不告诉你在哪
问题:
- URL:资源移动了,URL失效(404)
- URN:资源移动了,URN仍然有效(但需要解析服务)
为什么URN很少用?
阿西噶阿西:"因为URN需要额外的解析服务。"
使用URN的流程:
1. 客户端有URN:urn:isbn:978-7-115-54326-9
2. 查询URN解析服务:"这本书在哪?"
3. 解析服务返回URL:https://library.com/books/...
4. 客户端访问URL
问题:
- 需要维护URN解析服务(成本高)
- URL直接访问,简单方便
结果:
- URL被广泛使用 ✅
- URN很少使用(除了ISBN、DOI等学术领域)
南北绿豆:"所以实际开发中,99%用的都是URL。"
🎯 URI:统一资源标识符
URI是更广泛的概念
URI:
标识资源的字符串(总称)
包括:
- URL(通过位置标识)
- URN(通过名称标识)
示例:
https://www.example.com/api/user → URI(也是URL)
urn:isbn:978-7-115-54326-9 → URI(也是URN)
Spring为什么用URI?
哈吉米:"Spring的@RequestMapping为什么叫URI不叫URL?"
@RestController
@RequestMapping("/api/user") // 这是URI
public class UserController {
@GetMapping("/{id}") // 这也是URI
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}
南北绿豆:"因为@RequestMapping里只写了路径部分,不是完整的URL。"
完整的URL:
https://www.example.com:8080/api/user/123
@RequestMapping只关心:
/api/user/123 ← 这部分叫URI(或者叫URI路径)
不包括:
- 协议(https)
- 主机(www.example.com)
- 端口(8080)
严格来说:
| 名称 | 示例 | 说明 |
|---|---|---|
| 完整URL | https://example.com:8080/api/user | 协议+主机+端口+路径 |
| URI路径 | /api/user | 只有路径部分 |
| URI | 两者都是 | URI是总称 |
阿西噶阿西:"所以Spring用URI这个词更准确,因为它既可以是完整URL,也可以是路径。"
🎯 RESTful API中的URI设计
URI设计最佳实践
规范:
1. 使用名词,不用动词
✅ /api/users
❌ /api/getUsers
2. 使用复数
✅ /api/users
❌ /api/user
3. 层级关系
✅ /api/users/123/orders
❌ /api/getUserOrders?userId=123
4. 小写字母,用-分隔
✅ /api/order-items
❌ /api/OrderItems
❌ /api/order_items
5. 不要在URI中包含动词
✅ POST /api/users(创建用户)
❌ POST /api/createUser
6. 版本号
✅ /api/v1/users
❌ /api/users?version=1
示例对比
❌ 不好的设计:
GET /api/getUser?id=123
POST /api/createUser
POST /api/updateUser
POST /api/deleteUser?id=123
GET /api/getUserOrders?userId=123
✅ 好的设计(RESTful):
GET /api/users/123 # 查询用户
POST /api/users # 创建用户
PUT /api/users/123 # 更新用户
DELETE /api/users/123 # 删除用户
GET /api/users/123/orders # 查询用户的订单
Spring实现
@RestController
@RequestMapping("/api/users")
public class UserController {
// GET /api/users/123
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
// POST /api/users
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
// PUT /api/users/123
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user);
}
// DELETE /api/users/123
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
// GET /api/users/123/orders
@GetMapping("/{id}/orders")
public List<Order> getUserOrders(@PathVariable Long id) {
return orderService.getOrdersByUserId(id);
}
}
🎓 面试标准答案
题目:URL和URI有什么区别?
答案:
核心关系:
- URI(Uniform Resource Identifier):统一资源标识符(总称)
- URL(Uniform Resource Locator):统一资源定位符(URI的一种)
- URN(Uniform Resource Name):统一资源名称(URI的一种)
关系:
URI = URL + URN
URL ⊂ URI
区别:
| 特性 | URI | URL | URN |
|---|---|---|---|
| 概念 | 总称 | 通过位置标识 | 通过名称标识 |
| 示例 | 所有资源标识符 | https://example.com/user | urn:isbn:978-xxx |
| 是否包含位置 | 不一定 | ✅ 必须 | ❌ 不包含 |
| 使用频率 | - | ⭐⭐⭐⭐⭐ 常用 | ⭐ 很少用 |
举例说明:
https://www.example.com/api/user/123
→ 这是URI(资源标识符)
→ 也是URL(包含了位置:example.com)
→ 不是URN(URN是名称,不包含位置)
urn:isbn:978-7-115-54326-9
→ 这是URI(资源标识符)
→ 不是URL(没有位置信息)
→ 是URN(通过ISBN名称标识)
/api/user/123
→ 这是URI(资源标识符)
→ 不是完整的URL(缺少协议和主机)
→ 是URI路径
结论:所有URL都是URI,但不是所有URI都是URL。
题目:@RequestMapping为什么是URI不是URL?
答案:
@RequestMapping("/api/user/{id}")
原因:
- 只包含路径部分(
/api/user/123) - 不包含协议、主机、端口
- 不是完整的URL
- 但是URI(资源标识符的一部分)
完整URL vs URI路径:
| 类型 | 示例 |
|---|---|
| 完整URL | https://www.example.com:8080/api/user/123 |
| URI路径 | /api/user/123 |
| @RequestMapping | /api/user/{id}(URI模板) |
🎉 结束语
晚上9点,哈吉米终于搞清楚了URI、URL、URN的区别。
哈吉米:"原来URI是总称,URL是通过位置标识资源,URN是通过名称标识资源!"
南北绿豆:"对,URL是URI的子集,所有URL都是URI。"
阿西噶阿西:"记住:实际开发中99%用的都是URL,URN很少用。"
哈吉米:"还有@RequestMapping用的是URI路径,不是完整URL。"
南北绿豆:"对,理解了这些概念,面试就不会被问懵了!"
记忆口诀:
URI是总称标识符,URL位置URN名称
URL是URI子集,所有URL都是URI
URL包含协议主机端口路径,URN只有名称无位置
实际开发URL最常用,URN很少见
Spring用URI更准确,只有路径不是完整URL