本文是《Solo 从设计到实现》的一个章节,该系列文章将介绍 Solo 这款 Java 博客系统是如何从无到有的,希望大家能通过它对 Solo 从设计到实现有个直观地了解、能为想参与贡献的人介绍清楚项目,也希望能为给重复发明重新定义博客系统的人做个参考 ❤️

自定义链接路由

“自定义链接”指的是发布文章或者页面时指定的 URL path 部分,自定义链接主要是为了让 URL“好看”一些,对 SEO 也有一定帮助。

Solo 通过 Servlet Filter 实现了这个特性,在 PermalinkFilter.java 中,先根据请求的 URI 解析出 permalink,然后查库判断是否存在数据(可能是文章也可能是页面),如果不存在则走常规的请求分发:

final ArticleRepository articleRepository = beanManager.getReference(ArticleRepository.class);
article = articleRepository.getByPermalink(permalink);
if (null == article) {
    final PageRepository pageRepository = beanManager.getReference(PageRepository.class);
    page = pageRepository.getByPermalink(permalink);
}

if (null == page && null == article) {
    LOGGER.log(Level.DEBUG, "Not found article/page with permalink [{0}]", permalink);
    chain.doFilter(request, response);

    return;
}

如果存在数据记录,则通过方法 dispatchToArticleOrPageProcessor 直接分发给对应的控制器,数据记录放到请求属性中传递:

if (null != article) {
    request.setAttribute(Article.ARTICLE, article);
    request.setAttribute(Keys.HttpRequest.REQUEST_URI, Latkes.getContextPath() + "/article");
} else {
    request.setAttribute(Page.PAGE, page);
    request.setAttribute(Keys.HttpRequest.REQUEST_URI, Latkes.getContextPath() + "/page");
}
request.setAttribute(Keys.HttpRequest.REQUEST_METHOD, HttpMethod.GET.name());

Latke 框架在进行请求路由解析时会先从请求属性中获取 URI 路径,所以这里的设置操作相当于覆盖了原始的请求路径。然后在 ArticleProcessor#showArticle 方法中直接从请求属性中获取文章记录,随后再处理渲染:

// See PermalinkFilter#dispatchToArticleOrPageProcessor()
final JSONObject article = (JSONObject) request.getAttribute(Article.ARTICLE);
if (null == article) {
    response.sendError(HttpServletResponse.SC_NOT_FOUND);

    return;
}

final String articleId = article.optString(Keys.OBJECT_ID);
LOGGER.log(Level.DEBUG, "Article [id={0}]", articleId);

final AbstractFreeMarkerRenderer renderer = new SkinRenderer(request);
context.setRenderer(renderer);
renderer.setTemplateName("article.ftl");

...

优化考量

在过滤器中,为了优化性能,做了一个链接格式的判断:

if (PermalinkQueryService.invalidPermalinkFormat(permalink)) {
    LOGGER.log(Level.DEBUG, "Skip filter request [URI={0}]", permalink);
    chain.doFilter(request, response);

    return;
}

根据链接的格式可以判断出“明显不是”自定义链接的请求,这样就不用查库了,减少查库次数以优化性能。

另外,通过请求属性传递数据实体也是为了减少查库,既然在过滤器中查出来了,在控制器中就没必要再查库了。


回到全文目录:《Solo 从设计到实现》

感谢    关注    收藏    赞同    反对    举报    分享