- 后端
- 前端
- 社交
- 人工智能
【免费下载链接】NewsBlur
NewsBlur is a personal news reader that brings people together to talk about the world. A new sound of an old instrument.
NewsBlur 自 2011 年夏天开放公开 API 起,就建立了一套面向第三方开发者的外部 REST 接口:任何团队或个人都可以基于它构建移动应用、书签脚本(bookmarklet)、浏览器扩展甚至桌面客户端。本文以当年首个第三方 Android 客户端 Blar 的发布为引子,结合当前仓库中 API 的路由、视图实现与官方 Android 客户端代码,完整讲解 NewsBlur 开放 API 的认证机制、核心端点、第三方客户端接入方式,以及官方对第三方生态的收录与支持策略。读完本文,你将掌握如何基于apps/api与apps/reader的接口为自己的客户端接入订阅、阅读、收藏与分享能力。
一、背景:Blar——NewsBlur 生态中的第一个第三方 Android 客户端
2011 年 8 月 9 日,NewsBlur 官方博客发布公告(见 blog/_posts/tumblr/2011-08-09-blar-a-new-android-app-for-newsblur.md):第三方开发者 Harris Munir 打造的 Android 客户端Blar正式上架 Android Market(应用 ID 为bitwrit.Blar),成为 NewsBlur 移动生态的里程碑事件。这篇公告透露了三条对开发者至关重要的信息:
- Blar 完全基于"刚发布的 NewsBlur API"构建——这是 NewsBlur 首次公开 API 并验证了"第三方客户端"模式的可行性;
- Blar 的源代码以宽松的 fair license 开源,托管于公共代码托管平台,其他开发者可以直接阅读、借鉴其接入方式;
- 官方明确欢迎更多第三方作品:移动应用、bookmarklet、浏览器扩展、桌面客户端均可被收录,优秀作品会展示在每位用户的 dashboard 或 Goodies 专区;官方还承诺"很乐意协助开发,甚至按需新增 API 端点"。
同篇公告还提到官方 iPhone 应用已接近完成并进入内测阶段——premium 用户可申请免费测试副本。这条时间线说明:NewsBlur 的 API 策略从一开始就是"官方客户端与第三方客户端共享同一套开放接口",这套架构在今天的仓库中依然完整保留。
二、NewsBlur 开放 API 的整体架构
2.1 三个接口入口的挂载关系
在 newsblur_web/urls.py 中,对外开放的接口被挂载到三个路径前缀:
/api/→ apps/api/urls.py:面向"站外场景"的外部 REST API,主要服务 bookmarklet、浏览器扩展与移动客户端的认证和站外收藏/分享;/reader/→ apps/reader/urls.py:阅读器核心接口,包含订阅管理、故事流加载、已读/未读状态、星标收藏等全部阅读功能;/social/与/mobile/:社交互动(分享、评论、关注)与移动端辅助页面。
其中/api/模块的视图在 apps/api/views.py 顶部文档字符串中明确自述为"为第三方 NewsBlur 客户端提供的外部 REST API 端点,覆盖订阅、故事加载、分享与用户认证,面向移动应用与浏览器扩展"——这正是 Blar 当年所依赖接口的直系后代。
2.2 两类接口的职责分工
从源码结构可以清晰看出 NewsBlur 把"外部开放 API"与"站内阅读器 API"分开设计:
| 前缀 | 定位 | 典型端点 |
|---|---|---|
/api/ | 站外操作:登录注册、按 secret_token 收藏/分享故事、订阅网址 | login、signup、logout、share_story、save_story、add_site |
/reader/ | 站内阅读:订阅树、故事流、已读未读、星标、文件夹 | feeds、feed、river_stories、starred_stories、mark_story_as_read、add_url |
第三方客户端(尤其是移动 App)通常两者都要用:用/api/login完成认证,再用/reader/*拉取阅读数据、执行阅读操作。
三、认证机制:登录、注册与 secret_token
3.1 登录端点的源码级约束
/api/login的实现位于 apps/api/views.py。该视图对第三方客户端有三个值得注意的约束:
- 仅接受 POST:GET 请求会直接返回
{"code": -1, "errors": {"method": "Invalid method. Use POST. ..."}}; - 强制校验 User-Agent:请求头缺少
HTTP_USER_AGENT或 User-Agent 为nativehost时会被拒绝并记录日志("You must set a user agent to login."),这是官方为区分浏览器请求与程序化客户端请求所做的防护; - 成功/失败以
code字段表达:成功返回code: 1,失败返回code: -1并附errors字典,响应由@json.json_view装饰器统一序列化为 JSON。
/api/signup(apps/api/views.py)与/api/logout(apps/api/views.py)结构一致:signup 同样只接受 POST 并复用 reader 应用中的SignupForm,校验通过后直接登录;logout 则简单调用 Django 的logout_user后返回code: 1。注意登录/注册表单定义在 apps/reader/forms.py 中,第三方客户端发送username/password字段即可。
3.2 站外操作的 secret_token 机制
bookmarklet 等无法维护会话的场景,NewsBlur 提供基于secret_token的认证方式。用户 profile 中保存了独立的 secret_token(见 apps/profile/models.py),第三方脚本在 URL 中携带该 token 即可代表用户执行操作。典型用例:
/api/add_site/<token>:按 URL 添加订阅(apps/api/views.py),支持url、folder、new_folder参数,内部调用UserSubscription.add_subscription(..., bookmarklet=True);/api/share_story/<token>与/api/save_story/<token>:按 URL 分享或收藏故事;/api/check_share_on_site/<token>:查询某页面是否已被分享。
从 apps/api/urls.py 可以看到,这些带 token 的端点还额外提供了不带 token 的版本(如/api/share_story/?$),以便已登录会话的请求直接复用。share_story与save_story视图内部都会先检查request.user.is_authenticated,未认证时再回退到Profile.objects.get(secret_token=token)(见 apps/api/views.py),实现了"会话优先、token 兜底"的双通道认证。
四、阅读数据端点:订阅树、故事流与星标
4.1 订阅与文件夹
第三方客户端第一步通常是拉取用户的订阅树,对应端点/reader/feeds/(apps/reader/urls.py),视图load_feeds会返回用户的 feed 列表、文件夹结构、未读计数等信息。其底层数据模型是UserSubscription与UserSubscriptionFolders(见 apps/reader/models.py),后者以嵌套的 folders 结构维护"文件夹 → feed"的树状组织,并提供add_folder、move_feed_to_folder、rename_folder、delete_folder等方法,对应/reader/add_folder、/reader/move_feed_to_folders、/reader/rename_folder、/reader/delete_folder等端点。
4.2 故事流加载
阅读类端点集中在 apps/reader/urls.py:
/reader/feeds/:订阅树与未读计数;/reader/feed/<feed_id>:单个 feed 的故事列表;/reader/river_stories:跨 feed 的"河流视图"故事流,底层走 Redis 哈希缓存(视图load_river_stories__redis),由UserSubscription.feed_stories/story_hashes方法实现按read_filter(all/unread)与order(newest/oldest)取故事哈希,再批量取故事正文;/reader/starred_stories与/reader/starred_story_hashes:星标收藏列表及其哈希;/reader/read_stories:已读历史;/reader/unread_story_hashes:未读故事哈希,客户端可据此在本地标记哪些条目未读。
这些端点的行为有大量测试用例兜底,例如 apps/reader/test_river_stories.py 覆盖了河流视图的分页稳定性、未读过滤、日期过滤,以及免费用户第一页最多 3 条故事的配额逻辑(test_river_stories__free_user_first_page_is_capped_at_three_stories),第三方客户端开发者可据此了解接口的边界行为。
4.3 已读/未读状态同步
阅读器 API 提供了一整套状态变更端点(apps/reader/urls.py):
mark_story_as_read/mark_story_hashes_as_read/mark_feed_stories_as_read:标记单篇/批量/整 feed 已读;mark_story_as_unread/mark_story_hash_as_unread/mark_stories_as_unread:反向标记未读;mark_feed_as_read/mark_all_as_read:整 feed 或全部已读;mark_story_as_starred/mark_story_hash_as_starred/ 对应 unstarred:收藏与取消收藏。
底层写入逻辑集中在 apps/reader/models.py 的RUserStory.mark_read、UserSubscription.mark_story_ids_as_read等方法,并会同步更新 Redis 中的未读计数缓存。并发场景(如"标记已读与重算未读计数同时发生")的正确性由 apps/reader/test_unread_races.py 专门验证。
4.4 站外收藏与分享:/api/save_story与/api/share_story
这是 Blar 当年使用的核心站外端点,实现于 apps/api/views.py 与 apps/api/views.py。
save_story接受story_url(必填)与title(必填)、content、rss_url、user_tags、user_notes、feed_id等参数:
- 若传了
feed_id则直接定位 feed;否则依次尝试按rss_url、story_url解析 feed(Feed.get_feed_from_url,必要时create=True, fetch=True); - 若未提供
content,会回退到TextImporter(apps/rss_feeds/text_importer.py)抓取原文页面并抽取正文与标题,抽取失败则以空内容保存(content = "",避免整单失败); - 收藏记录写入 MongoDB 的
MStarredStory,并在保存后触发MStarredStoryCounts.schedule_count_tags_for_user重算标签计数; - 响应带
Access-Control-Allow-Origin: *与Access-Control-Allow-Methods: POST,为浏览器端 bookmarklet 跨域调用留好了 CORS 通道。
share_story逻辑类似,但把故事写入MSharedStory,同时通过MSocialSubscription标记订阅者的未读重算,并调用shared_story.publish_update_to_subscribers()实时推送分享事件给关注者。这两者都支持"带 token URL"与"已登录会话 POST"两种调用方式。
五、第三方客户端接入实证:官方 Android 应用的 API 消费方式
Blar 之后的官方 Android 客户端(clients/android)完整保留了"基于开放 API 构建客户端"的架构,其全部接口路径集中定义在 clients/android/NewsBlur/app/src/main/java/com/newsblur/network/APIConstants.java,是第三方客户端接入时的"现成清单":
| 常量 | 路径 | 用途 |
|---|---|---|
PATH_LOGIN | /api/login | 登录认证 |
PATH_SIGNUP | /api/signup | 注册 |
PATH_FEEDS | /reader/feeds/ | 订阅树 |
PATH_RIVER_STORIES | /reader/river_stories | 河流视图故事流 |
PATH_FEED_STORIES | /reader/feed | 单 feed 故事 |
PATH_STARRED_STORIES | /reader/starred_stories | 星标收藏 |
PATH_MARK_STORIES_READ | /reader/mark_story_hashes_as_read/ | 批量标记已读 |
PATH_SHARE_EXTERNAL_STORY | /api/share_story/ | 站外分享(带 token) |
PATH_SAVE_EXTERNAL_STORY | /api/save_story/ | 站外收藏(带 token) |
PATH_ADD_FEED | /reader/add_url | 添加订阅 |
PATH_DELETE_FEED | /reader/delete_feed | 删除订阅 |
PATH_MOVE_FEED_TO_FOLDERS | /reader/move_feed_to_folders | 移动 feed 到文件夹 |
PATH_EXPORT_OPML | /import/opml_export | OPML 导出 |
该文件还展示了两个对第三方客户端很有用的工程细节:
- 可配置服务器地址:
APIConstants.setCustomServer(newUrlBase)允许把 API 基地址从默认的https://newsblur.com切换到自建实例(对应登录界面 strings.xml 中的login_custom_server_hint),第三方客户端可以借此对接 NewsBlur 自托管部署; - 路径统一走
buildUrl(path)拼接:CurrentUrlBase + path,所有请求集中在一个入口,便于统一加认证头与超时控制。
六、第三方生态的官方支持策略与演化
回到 2011 年的公告,官方对第三方生态的支持承诺有三层,且均有长期延续的印证:
- 开放 API 并保持向后兼容:
/api/login、/api/save_story/<token>等端点从 Blar 时代延续至今,路径与参数结构基本稳定;share_story/save_story的 token 版本与登录版本双通道设计也一直保留在 apps/api/urls.py 中; - 帮助开发、按需新增端点:官方明确表示"愿意协助开发,甚至为第三方新增 API 端点"——从仓库中逐年新增的端点(如近期的
/ask-ai/question、/briefing/stories、/reader/cluster_stories等)可以看出这一承诺被持续兑现; - 收录与展示第三方作品:优秀第三方作品会出现在用户 dashboard 或 Goodies 专区。iOS 客户端(clients/ios)与 Android 客户端(clients/android)本身就是这条开放策略结出的官方果实,而浏览器扩展(clients/browser-extension)与 Safari 扩展(clients/safari-extension)同样消费这套 API。
对于今天想为 NewsBlur 构建客户端(或自托管实例的专属客户端)的开发者,最直接的接入路线是:用/api/login做认证(务必设置合理 User-Agent、使用 POST),用/reader/feeds/+/reader/river_stories拉取阅读数据,用/reader/mark_*系列同步已读状态,用/api/save_story/<token>与/api/share_story/<token>实现站外收藏与分享,最后对照官方 Android 客户端的 APIConstants.java 检查遗漏的端点。从 2011 年的 Blar 到今天的官方客户端,这套 API 始终是 NewsBlur"让第三方客户端繁荣起来"承诺的技术底座。
- 后端
- 前端
- 社交
- 人工智能
【免费下载链接】NewsBlur
NewsBlur is a personal news reader that brings people together to talk about the world. A new sound of an old instrument.
相关推荐
NewsBlur 第三方客户端生态解析:从 iOS 与 Windows 应用看开放 API 驱动的订阅流
NewsBlur 第三方客户端生态解析:从 iOS 与 Windows 应用看开放 API 驱动的订阅流 NewsBlur 是开源的个性化阅读器,其开放 RES
后端前端社交人工智能Convert to it Typst排版引擎分析:现代文档渲染如何在浏览器跑起来
Convert to it Typst排版引擎分析:现代文档渲染如何在浏览器跑起来 Convert to it 是一个真正"通用"的在线文件转换工具,它的全部转
后端前端社交人工智能Redwood 接入第三方 API 实战:基于 OpenWeather 构建天气查询应用(客户端与服务端双方案)
Redwood 接入第三方 API 实战:基于 OpenWeather 构建天气查询应用(客户端与服务端双方案) 导读 本文基于 Redwood 官方 How
后端前端Web框架开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考