using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace eShop.App_Start
{
public class AjaxCrawlableAttribute : ActionFilterAttribute
{
private const string Fragment = "_escaped_fragment_";
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
if (request.QueryString[Fragment] != null)
{
var url = request.Url.ToString().Replace("?_escaped_fragment_=", "#");
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary { { "controller", "HtmlSnapshot" }, { "action", "returnHTML" }, { "url", url } });
}
return;
}
}
}
这在‘ App _ start’中也被称为‘ filterConfig.cs’:
using System.Web.Mvc;
using eShop.App_Start;
namespace eShop
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AjaxCrawlableAttribute());
}
}
}
var page = require('webpage').create();
var system = require('system');
var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();
page.onResourceReceived = function (response) {
if (requestIds.indexOf(response.id) !== -1) {
lastReceived = new Date().getTime();
responseCount++;
requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if (requestIds.indexOf(request.id) === -1) {
requestIds.push(request.id);
requestCount++;
}
};
function checkLoaded() {
return page.evaluate(function () {
return document.all["compositionComplete"];
}) != null;
}
// Open the page
page.open(system.args[1], function () { });
var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if ((new Date().getTime() - lastReceived > 300 && requestCount === responseCount) || new Date().getTime() - startTime > 10000 || checkLoaded()) {
clearInterval(checkCompleteInterval);
var result = page.content;
//result = result.substring(0, 10000);
console.log(result);
//console.log(results);
phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 300);
首先,我要感谢 Thomas Davis,因为在这个页面中我得到了基本代码: ——)。
您会注意到这里有些奇怪的地方: 幽灵一直重新加载页面,直到 checkLoaded()函数返回 true。为什么?这是因为我的特定 SPA 进行了几次 AJAX 调用,以获取所有数据并将其放在我页面上的 DOM 中,并且在返回 DOM 的 HTML 反射之前幽灵无法知道所有调用何时完成。我在这里所做的是在最后的 AJAX 调用之后添加一个 <span id='compositionComplete'></span>,这样如果这个标记存在,我就知道 DOM 已经完成了。我这样做是为了回应 Durandal 的 abc2事件,请参阅 abc5了解更多。如果10秒钟内没有发生这种情况,我就放弃(最多只需要一秒钟)。返回的 HTML 包含用户在浏览器中看到的所有链接。脚本将无法正常工作,因为 HTML 快照中确实存在的 <script>标记没有引用正确的 URL。这也可以在 javascript 幻像文件中改变,但我不认为这是必要的,因为 HTML 快照只被谷歌用来获取 a链接,而不是运行 javascript; 这些链接 做引用一个漂亮的 URL,如果事实上,如果你试图在浏览器中看到 HTML 快照,你会得到 javascript 错误,但所有的链接将工作正常,并指导你到服务器再次与一个漂亮的 URL 这一次得到完整的工作页面。
就是这里。现在服务器知道如何处理漂亮和难看的 URL,并在服务器和客户机上启用推送状态。所有难看的 URL 都使用幽灵以相同的方式处理,因此没有必要为每种类型的调用创建单独的控制器。
您可能希望更改的一件事情是不要进行一般的“ type/subCategory/product”调用,而是添加一个“ store”,这样链接看起来就像: http://www.xyz.com/store/category/subCategory/product111。这将避免我的解决方案中的问题,即所有无效的 URL 都被视为实际上是对“ index”控制器的调用,我假设这些可以在“ store”控制器中处理,而不需要添加上面显示的 web.config。