Skip to content

TioBoot + Enjoy 生成 robots.txt 与 sitemap.xml:实战与SEO指南 ​

背景与目的 ​

为站点开放爬取并加速搜索引擎收录,robots.txt 与 sitemap.xml 是两块基础设施:

  • robots.txt:告诉搜索引擎哪些资源可以或不可以抓取。
  • sitemap.xml:提供站点重要页面(如内容详情页)的结构化清单,包含 URL 与最后更新时间,帮助搜索引擎更高效地发现与索引。

本文面向使用 TioBoot 与 Enjoy 模板 的开发者,提供一套可直接落地的实现方案,目标是:

  1. 在 https://preview.explanation.fun/robots.txt 返回“允许所有爬虫抓取”的内容,并在其中声明 sitemap.xml。
  2. 在 https://preview.explanation.fun/sitemap.xml 动态生成站点地图,自动包含首页与视频预览页列表。
  3. 方案与现有分页/预览架构保持一致,便于维护与扩展(例如未来加入标签页、专题页等)。

将获得什么 ​

  • 一个纯后端的 RobotsHandler:根据请求 Host 动态生成 robots.txt;
  • 一个纯后端的 SitemapHandler:从数据库查询视频记录,渲染 sitemap.xml;
  • 一个可配置/可扩展的 Enjoy 模板 sitemap.xml;
  • 简明的路由与接入方式,以及验证建议与SEO小贴士。

代码结构 ​

1) RobotsHandler:允许所有爬虫并指向 sitemap ​

java
package nexus.io.manim.handler;

import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.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 ​

java
package nexus.io.manim.handler;

import java.util.List;

import com.jfinal.kit.Kv;
import nexus.io.db.activerecord.Db;
import nexus.io.db.activerecord.Row;
import nexus.io.manim.consts.EfTableName;
import nexus.io.template.EnjoyTemplate;
import nexus.io.tio.boot.http.TioRequestContext;
import nexus.io.tio.http.common.HttpRequest;
import nexus.io.tio.http.common.HttpResponse;
import nexus.io.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
<?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
<?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>

路由接入示例 ​

java
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);

拦截器白名单(可选,若有拦截器):

java
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 小贴士(可选优化) ​

  1. 视频页 <title> 与 <meta name="description"> 把 topic/摘要 注入模版,利于点击与收录。

  2. lastmod 粒度 如需更精确,可用 ISO8601(例如 yyyy-MM-dd'T'HH:mm:ssXXX)。

  3. 分页列表也入站点地图 若希望列表页也被收录,可动态生成 /1/20, /2/20 等分页 URL 并加入 sitemap.xml。

  4. 缓存(视规模选择)

    • robots.txt 可直接常量返回;
    • sitemap.xml 可做分钟级缓存,减少 DB 压力。

总结 ​

  • RobotsHandler:返回“允许所有爬虫”,并指向 sitemap.xml;
  • SitemapHandler:查询数据库、渲染 Enjoy 模板,动态输出站点地图;
  • 零前端依赖,纯后端即可完成,有利于 SEO 与稳定收录;
  • 与现有分页/预览架构自然衔接,便于持续扩展与维护。

MIT Licensed