TioBoot + Enjoy 生成 robots.txt 与 sitemap.xml:实战与SEO指南
背景与目的
为站点开放爬取并加速搜索引擎收录,robots.txt 与 sitemap.xml 是两块基础设施:
- robots.txt:告诉搜索引擎哪些资源可以或不可以抓取。
- sitemap.xml:提供站点重要页面(如内容详情页)的结构化清单,包含 URL 与最后更新时间,帮助搜索引擎更高效地发现与索引。
本文面向使用 TioBoot 与 Enjoy 模板 的开发者,提供一套可直接落地的实现方案,目标是:
- 在
https://preview.explanation.fun/robots.txt返回“允许所有爬虫抓取”的内容,并在其中声明sitemap.xml。 - 在
https://preview.explanation.fun/sitemap.xml动态生成站点地图,自动包含首页与视频预览页列表。 - 方案与现有分页/预览架构保持一致,便于维护与扩展(例如未来加入标签页、专题页等)。
你将获得什么
- 一个纯后端的
RobotsHandler:根据请求 Host 动态生成robots.txt; - 一个纯后端的
SitemapHandler:从数据库查询视频记录,渲染sitemap.xml; - 一个可配置/可扩展的 Enjoy 模板
sitemap.xml; - 简明的路由与接入方式,以及验证建议与SEO小贴士。
代码结构
1) RobotsHandler:允许所有爬虫并指向 sitemap
package com.litongjava.manim.handler;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.util.Resps;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RobotsHandler {
private static final String ROBOTS = "User-agent: *\n"
+ "Allow: /\n"
+ "Sitemap: https://%s/sitemap.xml\n";
public HttpResponse index(HttpRequest request) {
String userAgent = request.getUserAgent();
log.info("userAgent:{}", userAgent);
String host = request.getHost(); // 例如 preview.explanation.fun
String text = String.format(ROBOTS, host);
HttpResponse response = TioRequestContext.getResponse();
return Resps.txt(response, text);
}
}
访问效果(示例):
User-agent: *
Allow: /
Sitemap: https://preview.explanation.fun/sitemap.xml
2) SitemapHandler:根据数据库数据动态生成 sitemap.xml
package com.litongjava.manim.handler;
import java.util.List;
import com.jfinal.kit.Kv;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Row;
import com.litongjava.manim.consts.EfTableName;
import com.litongjava.template.EnjoyTemplate;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.util.Resps;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SitemapHandler {
public HttpResponse index(HttpRequest request) {
String host = request.getHost(); // 例如 preview.explanation.fun
String DOMAIN = "https://" + host; // https://preview.explanation.fun
String userAgent = request.getUserAgent();
log.info("request:{}", userAgent);
HttpResponse response = TioRequestContext.getResponse();
// 查询所有已生成的视频(只需要 id 和 create_time)
String sql = "select id, create_time from " + EfTableName.ef_generated_video + " where video_url is not null";
List<Row> rows = Db.find(sql);
// 数据放入模板
Kv kv = Kv.by("domain", DOMAIN).set("videos", rows);
// 渲染 sitemap.xml 模板
String xml = EnjoyTemplate.renderToString("sitemap.xml", kv);
return Resps.xml(response, xml);
}
}
3) Enjoy 模板:sitemap.xml
放在你的模板目录(如
resources/templates/sitemap.xml)
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- 首页 -->
<url>
<loc>#(domain)/</loc>
<lastmod>#now("yyyy-MM-dd")</lastmod>
</url>
<!-- 视频预览页 -->
#for(video : videos)
<url>
<loc>#(domain)/preview/#(video.id)</loc>
<lastmod>#date(video.create_time, "yyyy-MM-dd")</lastmod>
</url>
#end
</urlset>
输出效果(示例):
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://preview.explanation.fun/</loc>
<lastmod>2025-09-07</lastmod>
</url>
<url>
<loc>https://preview.explanation.fun/preview/101</loc>
<lastmod>2025-09-05</lastmod>
</url>
<url>
<loc>https://preview.explanation.fun/preview/102</loc>
<lastmod>2025-09-06</lastmod>
</url>
</urlset>
路由接入示例
HttpRequestRouter r = server.getRequestRouter();
// robots.txt
RobotsHandler robotsHandler = new RobotsHandler();
r.add("/robots.txt", robotsHandler::index);
// sitemap.xml
SitemapHandler sitemapHandler = new SitemapHandler();
r.add("/sitemap.xml", sitemapHandler::index);
// 你已有的页面
VideoPageHandler videoPageHandler = new VideoPageHandler();
r.add("/{pageNo:\\d+}/{pageSize:\\d+}", videoPageHandler::index);
r.add("/preview/{id}", videoPageHandler::preview);
拦截器白名单(可选,若你有拦截器):
String[] permitUrls = {
"/", "/{pageNo:\\d+}/{pageSize:\\d+}",
"/preview/**",
"/robots.txt",
"/sitemap.xml",
"/ping"
};
new TioAdminInterceptorConfiguration(permitUrls).config();
验证方式
打开
https://preview.explanation.fun/robots.txt应返回:User-agent: * Allow: / Sitemap: https://preview.explanation.fun/sitemap.xml打开
https://preview.explanation.fun/sitemap.xml应看到包含首页与/preview/{id}的 URL 列表与<lastmod>。
SEO 小贴士(可选优化)
视频页
<title>与<meta name="description">把topic/摘要注入模版,利于点击与收录。lastmod粒度 如需更精确,可用 ISO8601(例如yyyy-MM-dd'T'HH:mm:ssXXX)。分页列表也入站点地图 若你希望列表页也被收录,可动态生成
/1/20,/2/20等分页 URL 并加入sitemap.xml。缓存(视规模选择)
- robots.txt 可直接常量返回;
- sitemap.xml 可做分钟级缓存,减少 DB 压力。
总结
RobotsHandler:返回“允许所有爬虫”,并指向sitemap.xml;SitemapHandler:查询数据库、渲染 Enjoy 模板,动态输出站点地图;- 零前端依赖,纯后端即可完成,有利于 SEO 与稳定收录;
- 与现有分页/预览架构自然衔接,便于持续扩展与维护。
