☰
基于 NewsBlur 开放 API 构建第三方阅读客户端:从 Blar 到官方 Android 应用
2026/9/29 5:38:51 网站建设 项目流程
  • 后端
  • 前端
  • 社交
  • 人工智能

【免费下载链接】NewsBlur

NewsBlur is a personal news reader that brings people together to talk about the world. A new sound of an old instrument.

项目地址:https://gitcode.com/gh_mirrors/ne/NewsBlur
点击查看免费下载

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 移动生态的里程碑事件。这篇公告透露了三条对开发者至关重要的信息:

  1. Blar 完全基于"刚发布的 NewsBlur API"构建——这是 NewsBlur 首次公开 API 并验证了"第三方客户端"模式的可行性;
  2. Blar 的源代码以宽松的 fair license 开源,托管于公共代码托管平台,其他开发者可以直接阅读、借鉴其接入方式;
  3. 官方明确欢迎更多第三方作品:移动应用、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_exportOPML 导出

该文件还展示了两个对第三方客户端很有用的工程细节:

  1. 可配置服务器地址:APIConstants.setCustomServer(newUrlBase)允许把 API 基地址从默认的https://newsblur.com切换到自建实例(对应登录界面 strings.xml 中的login_custom_server_hint),第三方客户端可以借此对接 NewsBlur 自托管部署;
  2. 路径统一走buildUrl(path)拼接:CurrentUrlBase + path,所有请求集中在一个入口,便于统一加认证头与超时控制。

六、第三方生态的官方支持策略与演化

回到 2011 年的公告,官方对第三方生态的支持承诺有三层,且均有长期延续的印证:

  1. 开放 API 并保持向后兼容:/api/login、/api/save_story/<token>等端点从 Blar 时代延续至今,路径与参数结构基本稳定;share_story/save_story的 token 版本与登录版本双通道设计也一直保留在 apps/api/urls.py 中;
  2. 帮助开发、按需新增端点:官方明确表示"愿意协助开发,甚至为第三方新增 API 端点"——从仓库中逐年新增的端点(如近期的/ask-ai/question、/briefing/stories、/reader/cluster_stories等)可以看出这一承诺被持续兑现;
  3. 收录与展示第三方作品:优秀第三方作品会出现在用户 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.

项目地址:https://gitcode.com/gh_mirrors/ne/NewsBlur
点击查看免费下载

相关推荐

上一篇:PT-depiler:一站式多站点PT资源管理工具终极指南
下一篇:3步掌握KataGo人类风格模型:从零开始的AI围棋陪练实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询