URL的进一步认识

URL这部分内容其实并不算难,最近刚好项目用到,做一个统一的处理


image.png

App开发中,鉴于上面的设计图,一般都是金刚区的内容,自己的项目整体经历了差不多3个过程

  • 移动端本地配置
    这种一般被用于项目初期,各种架构都不完善,为了赶项目进度,最为快捷方便的方式
  • 根据后端接口返回对应数据
    这种是根据后端接口返回对应数据,一般都是一个数组(图标、名称、其他隐藏数据)
  • 后端数据返回统一字段对应URL类型数据,移动端统一解析
    这种其实是对于上述返回数据格式的一种包装,通过URL的一种包装

下面是未包装之前的数据,其实完全够用,有很多公司其实一直也是这么用的,用同事的话说,能用但不够优雅

[
  {
    "sourceType" : "FTSC",
    "gmtCreated" : "2024-09-20 09:57:21",
    "paramData" : "0",
    "name" : "飞兔商城",
    "status" : 0,
    "jumpUrl" : "\/pages\/tabbar\/mall",
    "sort" : 1,
    "gmtModified" : "2025-03-21 13:49:40",
    "icon" : "2024\/12\/19\/cee2e20a098448c09600ac23d4cd4b11.png",
    "id" : 100012
  },
  {
    "sourceType" : "FTJD",
    "gmtCreated" : "2024-12-19 10:41:29",
    "paramData" : "",
    "name" : "飞兔酒店",
    "status" : 0,
    "jumpUrl" : "\/pages\/hotel\/index",
    "sort" : 2,
    "gmtModified" : "2025-03-20 16:06:41",
    "icon" : "2024\/12\/19\/f241260bce304de5a1c93431280ee1ae.png",
    "id" : 100324
  },
  {
    "sourceType" : "FTWM",
    "gmtCreated" : "2024-09-20 09:57:11",
    "paramData" : "",
    "name" : "飞兔外卖",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 3,
    "gmtModified" : "2025-03-20 16:06:54",
    "icon" : "2024\/12\/19\/eb3e350c17f946d6aaa78d3416115a5f.png",
    "id" : 100000
  },
  {
    "sourceType" : "FTCSH",
    "gmtCreated" : "2024-10-08 10:34:03",
    "paramData" : "",
    "name" : "飞兔车生活",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 4,
    "gmtModified" : "2025-03-21 14:36:04",
    "icon" : "2025\/03\/20\/e5ee3189cd8445c981ca9c4476ea61e3.png",
    "id" : 100138
  },
  {
    "sourceType" : "FTKD",
    "gmtCreated" : "2024-12-09 10:32:11",
    "paramData" : "",
    "name" : "飞兔快递",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 5,
    "gmtModified" : "2025-03-20 16:38:20",
    "icon" : "2025\/03\/20\/e06c082c11a44f4ea3adc57b5ca73989.png",
    "id" : 100321
  },
  {
    "sourceType" : "SHJF",
    "gmtCreated" : "2024-09-20 09:57:16",
    "paramData" : "",
    "name" : "飞兔缴费",
    "status" : 0,
    "jumpUrl" : "\/pages\/recharge\/phone",
    "sort" : 6,
    "gmtModified" : "2025-03-20 16:07:32",
    "icon" : "2024\/12\/19\/647bff6437eb4c54821f9fdc4ddbd70c.png",
    "id" : 100003
  },
  {
    "sourceType" : "FTBX",
    "gmtCreated" : "2024-10-08 10:39:07",
    "paramData" : "",
    "name" : "飞兔保险",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 7,
    "gmtModified" : "2025-03-20 16:38:35",
    "icon" : "2025\/03\/20\/7d2e7c65d3a946e8bfb7bae4fa0f338d.png",
    "id" : 100156
  },
  {
    "sourceType" : "FTJP",
    "gmtCreated" : "2025-01-13 23:27:41",
    "paramData" : "0",
    "name" : "飞兔机票",
    "status" : 0,
    "jumpUrl" : "\/pages\/tabbar\/ticket",
    "sort" : 8,
    "gmtModified" : "2025-03-21 14:35:33",
    "icon" : "2025\/01\/13\/9c60e750a30640d0820e6c938ad2945d.png",
    "id" : 100500
  },
  {
    "sourceType" : "JTKAPPLET",
    "gmtCreated" : "2024-12-05 10:55:55",
    "paramData" : "{\"actId\":105}",
    "name" : "景点门票",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 9,
    "gmtModified" : "2025-03-20 16:10:03",
    "icon" : "2024\/12\/19\/f7a471d29312401da5b934422f3c5dc2.png",
    "id" : 100305
  },
  {
    "sourceType" : "JTKAPPLET",
    "gmtCreated" : "2024-12-05 10:56:28",
    "paramData" : "{\"actId\":76}",
    "name" : "电影票",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 10,
    "gmtModified" : "2025-03-20 16:10:17",
    "icon" : "2024\/12\/19\/44740ee00e35436cb52b7580803f97d9.png",
    "id" : 100313
  },
  {
    "sourceType" : "FTHY",
    "gmtCreated" : "2025-03-25 16:46:59",
    "paramData" : "",
    "name" : "飞兔货运",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 11,
    "gmtModified" : "2025-03-25 16:48:53",
    "icon" : "2025\/03\/25\/1b21dd83e4d04e1bb00d5243e40fef3f.png",
    "id" : 100607
  },
  {
    "sourceType" : "FTTC",
    "gmtCreated" : "2025-03-25 16:46:41",
    "paramData" : "",
    "name" : "飞兔同城",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 12,
    "gmtModified" : "2025-03-25 16:48:57",
    "icon" : "2025\/03\/25\/0d80dd99d0a2407298e614c2e56fc4b0.png",
    "id" : 100603
  },
  {
    "sourceType" : "FTCDB",
    "gmtCreated" : "2024-12-05 10:54:38",
    "paramData" : "",
    "name" : "飞兔充电宝",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 20,
    "gmtModified" : "2024-12-19 10:42:20",
    "icon" : "2024\/12\/19\/245f41dd6f8445aeba75a0a268e1dc79.png",
    "id" : 100300
  },
  {
    "sourceType" : "JTKLONGH5",
    "gmtCreated" : "2024-10-08 10:30:23",
    "paramData" : "{\"actId\":152}",
    "name" : "生活团购",
    "status" : 0,
    "jumpUrl" : "",
    "sort" : 30,
    "gmtModified" : "2024-12-19 11:06:13",
    "icon" : "2024\/12\/19\/10f1df92bdcd4cc08a34840b88168588.png",
    "id" : 100114
  }
]
  • 对上面的隐藏数据类型简单说明下
    因为项目中这部分的交互复杂可变,有点击跳转小程序的,也有本地新页面,也有直接跳转本地H5页面,也有根据对应id获取对应数据,解析其中url跳转本地H5页面或者浏览器H5页面的,所有的这些的大前提都需要依据 sourceType 这个枚举类型,后期如果增加了新的类型,移动端就必须重新发包,苹果的话还需要再次经过审核,而且后端返回的字段相对比较冗余了,所以后面就想着最好可以一劳永逸的处理,后期新增或者删除只需要后端控制就好,这时候URL就提上日程

URl的初步认识

URL(Uniform Resource Locator)即统一资源定位符,它是互联网上用于定位和访问资源的地址

上面简单列举集中常见的url,一个完整的url通常由多个部分构成,基本格式如下
scheme://username:password@host:port/path?query#fragment

Scheme

  • 访问资源时具体使用的协议,一般有https/http/ftp 等,最常见的可能就是https/http 了
  • http 是超文本传输协议,https 是在 http 基础上加入了安全加密机制的协议,ftp 则用于文件传输。具体这里不多说,有需要可以自行了解

Username:Password

  • 可选部分,用于访问资源需要身份验证时的用户名与密码
  • 示例 ftp://user:pass@ftp.example.com,这里的 user 是用户名,pass 是密码
  • 作用 对用户进行身份验证,确保只有授权用户才能访问特定数据

Host

Port

  • 可选部分,用于指定服务器上提供服务的端口号
  • http 默认端口 80 ,https 默认端口 443, FTP命令端口:21、FTP数据端口(非加密):20(通常客户端随机选择,但服务器也可能配置为使用20)、FTP over SSL/TLS(隐式):990、FTP over SSL/TLS(显式):21(通过TLS加密,但仍使用21端口)、SFTP:22
  • 端口好用于区分不同的服务,客户端可以指定端口号链接服务器上特定的服务

Path

Query

Fragment

URL 示例

https://user:pass@www.example.com:8080/path/to/resource.html?key1=value1&key2=value2#section3

  • scheme: https
  • username: user
  • password: pass
  • host: www.example.com
  • port: 8080
  • path: /path/to/resource.html
  • query: key1=value1&key2=value2
  • fragment: #section3

最终经过进一步的数据结构调整,返回数据格式如下所示

  • 跳转flutter页面
{
      "id": 102,
      "icon": "2024/12/19/cee2e20a098448c09600ac23d4cd4b11.png",
      "name": "飞兔商城",
      "route": "feitu://native.feitu.com/mall",
      "sort": 1,
      "pid": 0,
      "isAuth": 1
}
  • 跳转微信小程序
{
      "id": 126,
      "icon": "2024/12/19/44740ee00e35436cb52b7580803f97d9.png",
      "name": "电影票",
      "route": "weixin://feitu.com/?username=gh_fc1db4789939&path=pages%2Findex%2Findex%2Findex&appid=wx4dea6f132ae99e01",
      "sort": 10,
      "pid": 0,
      "isAuth": 1
 }
  • 跳转H5
{
      "id": 145,
      "icon": "2024/12/19/10f1df92bdcd4cc08a34840b88168588.png",
      "name": "生活团购",
      "route": "redirect://jtk/152?target=WEB",
      "sort": 14,
      "pid": 0,
      "isAuth": 1
 }

总的来说,后端通过接口返回的这两种方式思路都是差不多的,将第一种返回多个字段合并为router一个字段,移动端根据URL解析做出不同的页面交互,为了后期扩展,预留字段pid(如果后期有多级,可以根据该字段获取下一级的数据),isAuth (该模块交互是否需要登录等),思路都是一样的,就是数据结构可能更优雅一点

这里使用swift简单封装一个解析的类

class FTURLParser {
    
    let scheme: String?
    let userName: String?
    let passWord: String?
    let host: String?
    let port: Int?
    let path: String?
    let query: String?
    let fragment: String?
    let queryItems: [URLQueryItem]?

    init?(urlString: String) {
        
        guard let url = URL(string: urlString),
              let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
            return nil
        }
        self.scheme = components.scheme
        self.userName = components.user
        self.passWord = components.password
        self.host = components.host
        self.port = components.port
        self.path = components.path
        self.query = components.query
        self.fragment = components.fragment
        self.queryItems = components.queryItems
        
    }

    //MARK: --- 根据query的key获取对应的value
    func getQueryValue(for key: String) -> String? {
        return queryItems?.first { $0.name == key }?.value
    }
}

具体使用如下

        let url = "https://user:pass@www.example.com:8080/path/to/resource.html?key1=value1&key2=value2#section3"
        
        let parser = FTURLParser(urlString: url)
        print("scheme = \(parser?.scheme ?? "")")
        print("userName = \(parser?.userName ?? "")")
        print("passWord = \(parser?.passWord ?? "")")
        print("host = \(parser?.host ?? "")")
        print("port = \(parser?.port ?? 0)")
        print("path = \(parser?.path ?? "")")
        print("query = \(parser?.query ?? "")")
        print("fragment = \(parser?.fragment ?? "")")
        print("value = \(parser?.getQueryValue(for: "key1") ?? "")")

结果如下


image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 目录 1 Nginx概述 2 使用 3 反向代理 4 负载均衡 5 重写 6 Nginx的其他用法 ...
    小小千千阅读 554评论 0 0
  • 在说 URL 之前,我们得需要先知道什么是 URI(Uniform Resource Identifier),统一...
    贪玩的木木阅读 598评论 0 2
  • Uniform Resource Locator 简称URL 它是因特网(internet)的万维网服务程序上用于...
    TmsGirafee阅读 1,319评论 0 0
  • 1 URL URL 是“统一资源定位符”(Uniform Resource Locator)的首字母缩写,形如ht...
    苏wisdom阅读 355评论 0 1
  • Linux 命令-curl 常用命令 下载单个文件 cur http://www.demo.com 默认将输出打印...
    majun00阅读 4,975评论 0 5