使用NestJS实现网站的RSS订阅

64 阅读2分钟

理解RSS的工作原理

RSS 是一种基于 XML 的格式,用于提供内容更新的订阅服务。RSS 订阅者通过读取你提供的 RSS 链接获取最新的内容更新。RSS 文件本质上是一个特定格式的 XML 文件,包含了文章的标题、链接、摘要、发布时间等信息。

作为一个前后端分离项目,我所有的博客都存放在后端数据库中,所以我需要在后端项目实现一个RSS订阅接口,来返回我更新的文章。

生成rss文件后,就可以和访问图片一样,直接访问指定地址的xml文件进行RSS订阅。

实现步骤

安装RSS库

npm install rss

实现RSS接口

import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
import * as RSS from 'rss';
import { BlogService } from './blog.service';
​
// RSS 订阅接口
  @Get('rss')
  async getRssFeed(@Res() res: Response) {
      ……
  }
初始化RSS订阅
const feed = new RSS({
    // RSS名称
    title: 'FlowersInk RSS Feed',
    // RSS介绍
    description: '订阅花墨的最新文章',
    // RSS订阅地址
    feed_url: 'https://flowersink.com/rss.xml',
    // 主站地址
    site_url: 'https://flowersink.com/',
    // 语言
    language: 'zh-CN',
    // 日期,设置为即刻
    pubDate: new Date(),
});
筛选博客

因为我的博客分为文章和问题两种类型,所以需要进行筛选。

又因为我使用的是我提前写好的博客搜索接口,所以需要必填其他筛选条件(填空即可)

// 筛选出 type 为 '文章' 的博客(获取全部符合条件的博客)
const { blogs } = await this.blogService.findAllBlog(1, 1000, {
    type: '文章',
    title: undefined,
    content: undefined,
    tag: undefined,
    date: null,
    star: undefined,
});
将每篇博客添加到RSS订阅
blogs.forEach((blog) => {
    feed.item({
        title: blog.title, // 博客标题
        description: blog.content.slice(0, 200), // 截取内容的前200字符
        url: `https://flowersink.com/blog/blog-detail/${blog.id}`, // 文章详情链接
        author: '再花', // 添加作者
        categories: blog.tag ? blog.tag.split(',') : [], // 使用 tag 作为标签
        date: blog.date, // 发布日期
    });
});
返回RSS订阅xml
// 设置响应头并返回 RSS 内容
res.type('application/rss+xml');
res.send(feed.xml({ indent: true }));

完整代码

import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
import * as RSS from 'rss';
import { BlogService } from './blog.service';
@Controller('blog')
export class BlogController {
    constructor(private readonly blogService: BlogService) {}
​
    @Get('rss')
    async getRssFeed(@Res() res: Response) {
        const feed = new RSS({
            title: 'FlowersInk RSS Feed',
            description: '订阅花墨的最新文章',
            feed_url: 'https://flowersink.com/rss.xml',
            site_url: 'https://flowersink.com/',
            language: 'zh-CN',
            pubDate: new Date(),
        });
​
        const { blogs } = await this.blogService.findAllBlog(1, 1000, {
            type: '文章',
            title: undefined,
            content: undefined,
            tag: undefined,
            date: null,
            star: undefined,
        });
​
        blogs.forEach((blog) => {
            feed.item({
                title: blog.title,
                description: blog.content.slice(0, 200),
                url: `https://flowersink.com/blog/blog-detail/${blog.id}`, 
                author: '再花',
                categories: blog.tag ? blog.tag.split(',') : [], 
                date: blog.date, 
            });
        });
​
        res.type('application/rss+xml');
        res.send(feed.xml({ indent: true }));
    }
}

为RSS订阅增加缓存

因为RSS并不需要频繁更新(我有可能一周才写一篇文章),那就可以为RSS增加一个缓存机制节省服务器资源。

使用NestJS内置的CacheModule实现就会非常简单。

安装相关依赖

npm install cache-manager @nestjs/cache-manager

配置缓存模块

import { Module, CacheModule } from '@nestjs/common';
import { CacheModuleOptions } from '@nestjs/cache-manager';

@Module({
  imports: [
    ……
    CacheModule.register<CacheModuleOptions>({
      ttl: 300, // 缓存时间(秒)
      max: 100, // 缓存最大条目数
    }),
  ],
})
export class AppModule {}

给方法配置缓存

import { Cache } from 'cache-manager';
export class BlogController {
    constructor(
    private readonly blogService: BlogService,
     @Inject(CACHE_MANAGER) private cacheManager: Cache,
    ){}

	@Get('rss')
	async getRssFeed(@Res() res: Response) {
    	// 检查缓存
    	let cachedFeed = await this.cacheManager.get<string>('rss_feed');
    	if (!cachedFeed) {
        // 初始化RSS等后续操作
    	}
	}
}

设置清理缓存逻辑

当发布新博客的时候,对缓存进行清理

// 创建新博客
async createBlog(createBlogDto: CreateBlogDto): Promise<Blog> {
    const newBlog = this.blogRepository.create(createBlogDto); // 创建博客实例
	await this.cacheManager.del('rss_feed'); // 清理缓存
	return this.blogRepository.save(newBlog); // 保存实例到数据库
}

验证

我们可以在网站:validator.w3.org/feed/ 来验证RSS订阅是否生效,如图所示

image.png