先解释一下路由接口,路由接口的功能就是 Typecho 根据客户端的访问请求,找到系统中对应的控制器,然后执行相关功能。可能比较难以理解,下面举个例子说明应该就比较看得懂了。
需求
当下使用较多的点赞功能应该就是 skylzl 大佬开发的 Like 插件了,实际使用中发现在 iOS 浏览器 Safari 环境下,存在可以无限次点赞功能。经排查发现,插件使用 Cookie 来识别是否执行了点赞动作,但是采用的 Cookie 插件对 Safari 支持不好,无法正确写入、读取 Cookie。
基于此问题背景,加上能少装插件就少装插件的目标,故考虑将 Like 核心功能集成到 ArmxMod for Typecho 主题中。
分析
点赞功能按实现流程,可以分为:
前端按钮点赞(JavaScript)--> 后端主题接收请求(PHP)--> 后端主题处理请求并反馈处理结果给前端(PHP) --> 前端根据反馈处理点赞结果(JavaScript)
实现
前端点赞
这部分比较简单,先指定点赞入口:
<a href="javascript:;" class="post-like" data-pid="'.$cid.'" id="pluszan">赞</a>
然后 JavaScript 绑定点击动作,并向后端发出请求:
var likeup = '/action/like?up';
$('.post-like').on('click', function(e){
var id = $(this).attr('data-pid');
$.post(likeup,{
cid:id
}
});
这里可以看到,我们使用了 Ajax 异步请求,所以应保证网站已经引入 jquery 库。
接收请求
这一部分即是路由接口需要做的事情,当前端点赞时,后端如何知道前端进行了点赞,参照代码:
function themeInit($self) {
......
if ($self->is('archive',404)){
$path_info = trim($self->request->getPathinfo(),'/');
if(strpos($path_info,'action/like') !== false){
header( "HTTP/1.1 200 OK" );
likeup($self);
$self->response->goBack();
exit;
}
}
......
}
路由接口需要在启用主题时就进行定义,所以应该在接口定义放在 Typecho function.php
的 themeInit
功能中。
这里我定义的路由是 /action/like
,如果请求匹配到,则执行点赞后端处理函数 likeup()
。
后端处理
后端主要处理的动作是,当接收到点赞时,对该文章的点赞数进行 +1
,并返回 success
标志告诉前端已经处理完成,参照代码:
function likeup($self) {
$cid = $self->request->filter('int')->cid;
$self->db = Typecho_Db::get();
$row = $self->db->fetchRow($self->db->select('likes')->from('table.contents')->where('cid = ?', $cid));
$self->db->query($self->db->update('table.contents')->rows(array('likes' => (int)$row['likes']+1))->where('cid = ?', $cid));
$self->response->throwJson("success");
}
前端处理
前端处理这一部分可有可无,如果需要针对后端处理结果,在前端做出响应,就要进一步处理,如果不处理也无大碍就是。需要注意的是,后端处理可能会花费较长时间才会有响应,造成前端困扰,因此鉴于是否响应无关紧要,我们可以直接在前端点赞时就给予反馈,比如提示“点赞成功”等等。
最后,本文中的范例仅作为参考,实际使用应根据环境做相应修改,比如增加请求校验等等。
Q&A
1、Q:为什么已经定义了Header("HTTP/1.1 200 OK");
返回的状态码还是 404 Not Found
?
A:在 Typecho 1.1 版本之后,对状态返回逻辑做了修改,任何类似 Header("HTTP/1.1 200 OK");
的状态码修改方法都是无效的,具体可参见 Github Issue。解决方法就是利用 Typecho 原生函数来实现:
原来的写法:
header( "HTTP/1.1 200 OK" );
likeup($self);
$self->response->goBack();
exit;
Typecho 1.2 及之后的新写法:
Typecho_Response::getInstance()->setStatus(200)->respond(likeup($self));
参考文章:
1、《Typecho中的路由解析》
2、《非插件实现路由[转]》
Mac OS X 10_13_6Chrome 79.0.3945.88来自 江苏 的大神
接收请求 这段是放到function.php 的 themeInit 里,后端处理 处理这段放哪里的额,博主大大,我加进去前台页面显示 Call to a member function is() on null,是啥原因额,求教,谢谢
function.php 里随便找个地方放都可以,最简单的就是放到最后
Mac OS X 10_13_6Chrome 79.0.3945.88来自 江苏 的大神
加好后还是显示Call to a member function is() on null
改个函数名试试
Windows 10Chrome 83.0.4103.116来自 日本 的大神
前端点赞那里的post是向likeup发送,路由接口写的是action/like,前端后端的接口路径应该怎么对应的写,我这样写post的返回值都是404页面
第三行post的括号好像没闭合
前端是向 like?up 发送,代码没拷全
Windows 10Chrome 83.0.4103.116来自 日本 的大神
我写成对应的接口,确实是执行了接口下的命令了,但是这个url指向的是一个404页面,主题初始化函数themeInit里面的$self指向的也是404页面的对象,没有cid属性啥的
所谓自定义路由就是使用原本不存在的 URL,所以指向 404 是正常的,但是写完自定义路由还是指向 404,说明这个 URL 没有被 Typecho 截取到。
需要确认一下前端向哪个 URL 发起请求,后台截取的是不是这个 URL
Windows 10Chrome 83.0.4103.116来自 香港 的大神
指向是指向404,此时的$this指的也是404页面的对象,但是命令倒是都执行了。看F12后台执行一次就要加载一次404页面,sidbar footer之类的也要完全加载,开销还是蛮大的,我最后还是另开了一个独立页面只加载header来实现了。
加载 404 页面,说明路由没有添加成功~~~
Mac OS X 10_15_2Chrome 79.0.3945.130来自 香港 的大神
我虽然是返回404页面(不管是post还是直接访问,返回码都是404,返回内容也都是动态的404.php页面),但是路由接口下除了那个200状态码的命令其他都正常执行了,post也正常传值了。现在就是好奇如果搞对的话返回的是哪个页面,而此时声明$this的话指向的又是哪个对象
Windows 10Chrome 83.0.4103.116来自 美国 的大神
哦,应该是和pjax那样子的一样,我再试试
返回的是你写的这个路由,执行的函数值
Windows 10Chrome 114.0.0.0来自 广东 的大神
请问一下这个返回状态码是404的有解决了吗?
我也是不管怎么弄,都是返回404,按道理header("HTTP/1.1 200 OK");这句是设置返回的状态码是200才对,可是就是没有改变状态码
你用的 Typecho 是 1.2 版本吧 ,1.1 之后的版本对方法做了修改,类似 header("HTTP/1.1 200 OK") 这种修改状态码的写法,都是无效的。方法稍后我写到文章里
Windows 10Firefox 80.0来自 上海 的大神
抄了半天没成功,原来是后端处理那里漏了一个括号
正常正常,想想那种漏掉的是一个点
TYPECHO升级版本,文件修改记录 - 天涯孤旅