Solid开发教程——概要

renyuneyun 2024年07月14日(周日) 1 mins

SoLiD(Social Linked Data)万维网发明者Tim Berners-Lee提出的以Web为基础的、以用户为中心的Web 3.0概念。本博客前面有几篇文章对其进行了一定介绍(如《Solid——简介与体验》《搭建Solid Community Server》),也尝试解释了一些理念。

然而由于Solid和当今的Web 2.0的理念不同,并且以RDF / Linked Data(LD)为数据基础,它的开发对一些人来说有不少门槛。再加上现有文档存在不好理解(比如许多文档是协议Spec而并非常见的例子)或者碎片化的问题,开发的上手难度则会更高。本文(本系列文章)将尝试对其中最核心的一些内容进行梳理,方便新开发者迅速把握要领——尤其是以中文为母语的开发者,毕竟大部分文档都是英文。

当前这篇会整体性地梳理一些最核心的理念,而后续文章则会针对一些具体的话题(比如现有的库或LD等领域)分别。

由于Solid仍然在演进,本文会以现有标准为基础进行讨论。在个别地方,如果某个已知即将进入标准的功能会带来改变(一般是积极改变),我也会将其纳入讨论。

Solid的Web 3.0和Web 2.0开发的核心区别

如果用一句话来概括Solid的Web 3.0和现今的Web 2.0的区别,那么就是:Web 3.0是以用户为中心,而Web 2.0是以服务为中心。

这里我仍然带上前缀说「Solid的Web3.0」,是因为它存在和区块链所称的Web3之间进行混淆的可能。本博客之前有一篇文章《论Web3.0和Web3》对此进行了梳理和讨论。由于我更认同Solid的Web 3.0而非区块链的,而且本文介绍的是Solid,所以后文将不再进行区分。

Web 3.0和Web 2.0本身的不同

在说开发区别前,我们首先要说明二者背后概念的区别。

Web 2.0下,我们做任何事要首先访问一个站点(比如google.com),然后在其上完成后续所有功能;如果需要存储数据,那么需要在该站点上注册账户/身份(比如谷歌账号),然后将数据存放在该站点的服务器中。多数情况下我们其实不怎么区别站点的服务器和其相关的其他服务器,而仅仅将它们统称为平台(毕竟我们作为用户也没法控制)。一个平台背后是一个服务商(比如谷歌),而一个服务商可能同时运行多个站点(比如谷歌同时运行www.google.com、play.google.com等站点,StackExchange同时运行stackoverflow.com、superuser.com等站点)。一般情况下一个服务商的站点数据(用户身份)会做到互通。但如果想要跨服务商?抱歉,做不到。

最近这些年随着例如OpenID/OIDC的SSO(Single Sign-On,单站登录)机制在一些站点受到支持,该现象得到了一点改善:用户不需要在每个网站输入自己的密码了,而只用选择使用一个SSO账户(比如GitHub)登录;用户暱称一般也都可以直接复制过来。但本质上,绝大多数站点背后仍然使用相同的逻辑:站点数据存储在本站的服务器中,而非SSO账户背后的服务器中(比如GitHub)。

虽然这里说是Web 2.0,但这个模式深刻影响了这个时代的绝大多数服务,也包括非(狭义)Web的在线服务,比如类似QQ、Telegram的各种即时通信软件,各种网络游戏等。

而Web 3.0下,用户拥有自己的身份(WebID)、拥有自己的数据存储库(Solid Pod)。当需要使用某个功能时,会去访问一个Solid App(比如https://umai.noeldemartin.com),然后在该Solid App上登录自己的WebID;该App会从用户的Solid Pod中存取数据,而非在自己的服务器上。这样,一个WebID可以用在所有的App上,而不需要重新注册账号;Solid Pod中的一套数据也可以用在所有App中,而不用重新输入;用户***可以自己决定Pod在哪里。

Solid之所以使用RDF/LD为主要数据结构,也是为了更好地支持跨App的数据共享,也就是数据的互操作性(interoperability)。另外,很理所当然的,一个人可以有多个WebID,一个WebID也可以有多个Solid Pod。这些灵活性都是Solid所追求的。

Web 3.0和Web 2.0开发的不同

从上面讨论中,我们其实已经看到了Web 3.0和Web 2.0开发的核心区别:Web 3.0中不再具有Web 2.0中包揽并控制全部的「平台」,而是将其分解为了提供功能的App、提供身份的WebID和提供存储的Solid Pod。

换句话说:Web 3.0开发中,开发者只需要开发App,而不用为了许多基础功能(比如用户身份、存储)而开发服务端内容。这对开发者和用户来说都是好事:

  • 开发者可以减少对用户身份(用户名表、密码混淆)、安全(数据泄漏)等方面的考虑或者顾虑,因为这都不再托管在自己的服务器上;
  • 用户数据存储在用户自己的Pod中,可以随时中断App对数据的访问,用户可以更容易信任App;
  • 数据已经存在Pod中,所以用户可以随时使用相兼容的其他App访问同一数据而不用担心更换App丢失数据,而开发者也同样可以得益于已有数据而避免冷启动(Cold Start)的问题。

Solid鼓励ephemeral(转瞬即逝的、生命短暂的)App,即一个App自己不存储数据,所有用户数据全部读取自和存储在用户的Pod中。典型的比如加载一个(静态)站点到浏览器中,然后所有功能全部由JS实现,数据存储在Pod中,或将缓存储存在浏览器中(注意这些缓存可以随时抛弃重建)。

另外,以用户为中心其实带来一个副作用,那就是我们明确认识到App不具有「全局」视角或「全部」数据——其实在Web 2.0中也是一样的,因为一个站点只有自己服务商数据库中的数据而不具有其他服务商数据库中的数据,但大家某种程度上选择性地忽略了这个事实。如果需要多人的数据,那就需要动态/即时从多个用户的Pod中读取数据。这可能带来效率问题,所以有人尝试使用各种方案的缓存来改善效率。本质上这和使用CDN或其他缓存节点改善Web 2.0没有太大区别,但Solid上有时需要考虑隐私等问题(Web 2.0则不需要考虑,因为不行),可能更复杂一些。

什么是Solid App?和Solid Pod怎么交互?

作为一个开发者,在Solid上进行的开发一般都是指Solid App的开发。而如其名称所示,Solid App就是遵从Solid协议的App(应用程序)。

如前面已经梳理的那样,Solid App是将原先捆绑在一起的业务、身份和存储三者拆分开后,「业务」这个概念的体现。这一拆分其实解放了开发者,因为这样App开发者只需要操心App本身的业务逻辑,而不用操心数据存储和用户身份维护等问题。这对小型团队尤其有利,因为可以省去处理安全、法规等的成本,也可以省去一大笔维护用户信息服务器的成本;并且,由于数据的互操作性,新开发者往往不需要担心冷启动的问题,因为可以直接使用Pod中的已有数据。

由于Solid是以Web为基础,典型状态下一个Solid App就表现为一个网页/网站,通过浏览器访问。用户从该App登录他的WebID,然后App从WebID中获取用户信息,包括Pod位置,然后在有需要的时候从(向)Pod中读(写)数据。

不过其实Solid协议并没有强制规定Solid App的实现形式,所以一个Solid App也完全可以是其他形式,比如一个手机App。

Solid Applications这个页面整理了一些以网页实现的Solid App,而澳大利亚Solid社区开发的许多Solid则是Web、桌面、智能手机全端支持。

从底层来说,Solid App和Solid Pod的交互是通过Solid协议。当然,有大量的现成的库来协助开发者,让我们不用去探究那么底层的事情,而只用关注高层的功能。所以只要掌握了Solid或者说Web 3.0的理念,Solid App开发和Web 2.0的(Web) App开发没有太大区别。

RDF和数据格式

Solid的一个特点是围绕RDF构建,推荐数据存储成RDF结构,比如序列化成TurtleJSON-LD文档,因为这可以带来最大化的数据互操作性。但Solid并不强制要求数据以RDF结构存储,所以也完全可以存放其他类型的数据——其实有些数据类型天生就不合适用RDF存储(比如位图),Solid很自然地不可能不去试图支持它们。

但在条件允许的情况下,我还是推荐尽量使用RDF来存储数据。除了互操作性及RDF相关工具的帮助外,RDF结构的数据本身也很清晰易懂,对App的后续维护也有帮助。对不熟悉RDF的人来说,或许最简单的一种过渡方案就是首先将数据存储为JSON,日后再将其迁移为JSON-LD。本文不深入讨论这点,等后续文章写完了会在此放置超链接。

RDF的各种序列化格式(比如JSON-LD和Turtle)都是可以自动互相转换的,Solid原生支持这点,只需要在HTTP请求头中声明自己需要的格式即可。

对RDF、LD等知识感兴趣的读者可以看我的另一篇文章《语义网和关联数据(浅层)知识梳理》。本文不展开讨论。

WebID和用户身份

作为用户身份标识,WebID有环绕其而存在的一些列标准或协议(例如Solid-OIDC)。对于开发者来说,除非你的App功能里包含修改WebID内容的需求,否则你的App只会涉及读取用户提供的WebID,而不包含写入或创建它。

一般情况下,当在某个Solid服务上注册账号后,它都会自动给你(用户)生成一个WebID,并存放在你的Pod中。取决于具体的Solid服务提供方和软件,你可能有机会为同一个账号额外创建更多的WebID(比如从v7.0开始的Community Solid Server就支持这个功能)。

WebID以一个URI(例如URL)来标识,而这个URI(当对它执行GET方法后)对应的则是一个有特殊规定的RDF文档。它的具体内容规定参见Solid WebID Profile,但大体来说包含如下内容:

  • 如何鉴别用户身份真伪
    • 比如solid:oidcIssuer
  • 用户的信息
    • 比如foaf:name
  • 用户的Solid Pod(们)在哪
    • pim:storage
  • 用户的数据相关的额外信息
    • 比如solid:publicTypeIndex

从功能上看,WebID是用户最核心的文档,也是App去「了解」用户的入口。Solid App需要/应该从WebID的内容去「发现」或者说「识别」用户的其他信息,比如如何鉴别用户身份,以及用户的Pod在哪里等。 因此,当登录Solid App时,一种常见的方式就是要求用户输入其WebID,然后App自动发现对应的Solid-OIDC服务,然后重定向过去进行后续的步骤(另一种就是要求输入Solid-OIDC提供商地址,然后重定向过去,进行后续)。

有一点值得注意:许多Solid服务自动生成的WebID会形如https://a-solid-service/profile/card#me,其中的#me这个anchor是必要的,不能省略。这个WebID它是指向了https://a-solid-service/profile/card这个RDF文档中叫做#me的一个节点,而这个#me节点才是真正的WebID Profile结构。

最后,WebID文档未必要放在Solid Pod中。许多Solid服务将它放在Pod中只是出于习惯和方便考虑,而另一些(比如ESS)则将它放在Pod之外。放在Pod中的确存在一定潜在安全风险(比如这个讨论),社区仍在讨论更理想的解决方案。但这都并非App开发者需要在意的问题,除非开发者使用了非标准的方式来从WebID Profile中获取信息。

Solid Pod提供的功能

既然Solid Pod是Solid的一个核心,而且是归属用户的,那么我们(尤其是作为开发者)有必要知道一个Solid Pod本身提供什么功能,以便合理区分哪些功能是App要提供的,哪些是直接交给Pod完成的。

如果作为Pod的所有者,如果我想去查看Pod的内容,还是需要使用一个Solid App来完成。一般情况下,一个Solid服务也会提供一个默认的界面供用户使用(本质上就是这么样一个Solid App),比如Mashlib或者叫SolidOS。如果想尝试别的,这个列表列举了一系列进行Pod管理的App。

大体来说,Solid Pod提供这么一些核心功能:

  • 身份认证——Solid-OIDC
  • 数据管理——增删改查
    • 对于RDF数据,还有额外的功能,比如:
      • 请求时对各种RDF序列化格式的自动转换
      • 直接对RDF文档进行patch(增量修改)
  • 文件组织——类似文件系统的Container和Resource的层级组织
  • 权限管理和检查——设置某个资源可以和不可以被谁(什么身份)使用
    • 权限划分包括读、写、控制等
    • 目前在从WACACP迁移
  • 外部输入——Inbox机制
    • 本质上Inbox是特化的一个具有(公众或特定人)可写权限的Container,但它作为LDP的通用机制存在
  • 实时通知——订阅通知频道

可以看到,对于构建一个App来说,所有关于数据维护和身份认证的功能都已经由Solid Pod提供,而App只要操心业务逻辑即可。

同时我们也可以看到,Solid作为Web 3.0拥有一个区块链所难以完成的优势:存储和分享私有数据。其核心就在于数据存放在用户的Pod中,而非公开的区块链上;权限检查和授予也发生在Pod中,而非区块链上的智能合约;这二者都可以随时随意修改。

这些全部都是Solid协议所规定的能力。其中部分能力由单独的协议或规范所规定,例如更早就标准化的LDP,或是Solid自己制定的Solid Notification。至于它们的细节和使用,请参阅相关文档、其他人的文章或本系列后续文章。

常见库

有许多现成的库和工具都在尝试帮助Solid开发和调试,其中有的本身就是为Solid开发,也有的是围绕LD而同时支持Solid,还有的是其他泛用库且(优先)支持Solid。这里简单列举一些我觉得有必要知道的库(以及我的评价):

  • 用户登录
    • Inrupt的Solid Clientsolid-client-authn
      • 有面向浏览器和面向nodejs的两款
      • 最常用的登录相关库
  • Solid数据管理
    • Jeff的Solid File Client
      • 以文件和文件系统的概念操作Solid Pod
      • 取决于用途,有时可能很好用,有时可能不合适
    • Inrupt的Solid Client
      • solid-client用来读写数据
        • 很低层,我个人不太推荐
      • solid-client-access-grants用来编辑权限
        • 同时支持WAC和ACP,包括同时兼容二者的接口
  • RDF查询和编辑
    • Comunica:一个全能的SPARQL工具
      • 它会自动决定是在客户端还是服务端运行SPARQL,很通用
    • LDO (Linked Data Objects):一个将LD数据映射为TS/JS object的库
      • 它的理念我很喜欢,既自然又好用
      • 但需要先写Shape(形状),对初学者不见得友好(但开发者也在尝试通过提供形状仓库来改进)
      • 另外有个有些类似的库LDflex,但似乎维护不太好
    • Soukai:一个ORM类的库,将RDF数据映射为TS object
      • 理念更接近传统的(关系型数据库的)ORM
      • 使用TS的class来定义数据结构,而非像LDO一样使用Shape来定义
    • rdf.js:定义并统一了在JS上操作RDF数据的接口和操作,并列举了很多在此基础上可互操作的库
      • 当需要直接处理具体的RDF文档时很好用,但不需要这种低层操作时不推荐

如前文所述,RDF不是必须,所以暂时不打算使用RDF的话可以先不看所有RDF相关库。如果想学RDF(我个人十分推荐),或者已经熟悉RDF但想更多地了解相关工具,可以考虑看一看https://rdfjs.dev/这个网站,它对JS上可用的RDF工具进行了整理和说明(未必都是围绕Solid的库,但很多都支持Solid)。

寻求帮助和参加活动

Solid社区十分友好,并且很原意帮助新来者,所以有问题可以直接去社区询问。官网的这个页面对此有总结。主要的渠道有这些:

  • Solid论坛:一个基于Discourse的论坛,可以问各种问题
  • Matrix空间(各种群组):Solid的各种聊天群,包括特定主题的群组,也包括更广泛的帮助群组

另外,Solid社区也有各种活动:

  • Solid World:面向公众的webinar,会有人讲一些他们在Solid上做的东西
    • 前两年是每月一次,但今年由于种种原因并不稳定
  • 日常线上聚会:各个开发组或主题的聚会,一般一周或两周一次,一般都在Jitsi(jit.si)平台上
    • 我个人是先参加的SolidOS聚会,然后和人们混熟了
    • 现在有了主题更明确的Solid Practitioner聚会,面向围绕Solid的开发者

当然,这些都是英文的,可能会有一定心理障碍。但其实社区里(尤其是论坛或聊天群里)不少人英语也都不怎么好,所以不用担心。至于中文渠道,我知道有Solid中文网,但没能加入群组(我不怎么用QQ和Telegram),所以不确定是否仍然活跃。但如果你有什么问题而且需要用中文提问,我很乐意尽量解答。最推荐邮件,但也欢迎从任何渠道联系我——发现联系我的渠道其实不难,但也不是傻瓜式的。


Related posts:

您可以在Hypothesis上的該群組內進行評論,或使用下面的Disqus評論。