Laravel应用AJAX无限滚动调用失败的原因及修复方案
Laravel 8博客评论无限滚动Bug排查与修复
问题背景
我正在开发一个基于Laravel 8的博客应用,在ArticlesController的show方法中负责展示单篇文章及对应的评论列表,视图层已实现评论列表的渲染,路由也配置好了文章的访问路径。为了将评论的分页功能替换为无限滚动,我编写了对应的JS代码,但遇到了如下问题:
直接访问带page参数的文章URL(例如https://larablog.com/show/deserunt-qui-exercitationem?page=2)时,页面能正常显示对应页码的评论,但Chrome控制台会抛出500内部服务器错误,错误追踪到ArticlesController中的$article = Article::firstWhere('slug', $slug)代码行,提示“Trying to get property 'id' of non-object”,而通过AJAX访问时这段代码却能正常运行。
1. Bug成因
- 直接访问带
page参数的URL时,Laravel分页组件默认会将当前请求的所有查询参数(包括page)附加到分页链接中,无限滚动JS代码可能在请求下一页评论时,错误地将page参数的内容当成了文章slug的一部分,导致控制器中$slug变量被赋值为错误的值,最终Article::firstWhere('slug', $slug)查询不到对应文章,返回null,后续代码尝试访问null的id属性时就抛出了错误。 - 另一种可能是路由定义中,文章slug的捕获规则没有做限制,当URL中的
page参数被解析时,意外干扰了slug的取值,比如路由参数与查询参数混淆,导致$slug获取到了错误内容,无法查询到有效的文章实例。
2. 最简单的修复方案
方案一:控制器内区分AJAX与非AJAX请求逻辑
在ArticlesController的show方法中,先判断请求是否为AJAX,再分别处理评论分页逻辑,同时提前校验文章实例是否存在:
public function show($slug) { // 查询文章,不存在则直接返回404 $article = Article::firstWhere('slug', $slug); abort_if(!$article, 404); if (request()->ajax()) { // AJAX请求仅返回评论列表片段 $comments = $article->comments()->paginate(5); return view('comments.list', compact('comments')); } // 非AJAX请求渲染完整文章页面,默认加载第一页评论 $comments = $article->comments()->paginate(5); return view('articles.show', compact('article', 'comments')); }
方案二:修正JS请求的URL格式
修改无限滚动的JS代码,确保请求下一页评论时,URL正确指向文章的show路由,仅传递必要的page参数,避免干扰slug解析:
let currentPage = 1; window.addEventListener('scroll', function() { if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { currentPage++; // 确保请求URL中的slug正确,仅附加page参数 fetch(`/show/{{ $article->slug }}?page=${currentPage}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(response => response.text()) .then(html => { document.getElementById('comments-container').insertAdjacentHTML('beforeend', html); }); } });
内容的提问来源于stack exchange,提问作者Razvan Zamfir
相关产品推荐
相关产品推荐

