如何将ASP.NET MVC控制器操作的结果添加到捆绑包中



我有一个返回JavaScript文件的控制器操作。我可以从我的视图中引用此文件,并且可以正常工作。我想将其放在System.Web.Optimization.bundle中

我正在尝试这样做,本质上是:

new Bundle().Include("~/DynamicScript/UrlDictionary");

我的捆绑包中的其他文件被渲染得很好,但是这个文件被忽略了。看到这种行为,我的假设是在应用程序能够通过路由基础架构解决URL之前就可以处理捆绑,或者捆绑组件不会以允许该解决方案的方式请求文件。

如果有人可以为我确认和/或在这里指向我的方向很好,这将不胜感激。

我认为这是很可行的 - 但是在进入解决方案之前 - 请记住,该捆绑包是在第一个命中上创建的,并重复使用 - 这意味着任何"动态"脚本仍然具有要成为全局(即它不能取决于特定的用户等)。这就是为什么它通常仅允许静态JS文件的原因。说...我可以想象您可能想在JS中粘贴诸如版本号之类的变量的情况(尽管在这种情况下,我个人只会使用Ajax/json来获取它)。

我认为它的方法是从捆绑包中创建派生类型。在其中,您将覆盖枚举方法。最初,您将在基础上枚举。总结文件,然后包括您自己的虚拟文件。

类似(注意:未测试的代码):

public class VirtualMethodBundle : Bundle
{
    private List<VirtualFile> _virtualContent = new List<VirtualFile>();
    public override IEnumerable<VirtualFile> EnumerateFiles(BundleContext context)
    {
        foreach(var file in base.EnumerateFiles(context))
        {
            yield return file;
        }
        foreach(var virtual in _virtualContent)
        {
            yield return virtual;
        }
    }
    public void AddCustomFile(VirtualFile file)
    {
        _virtualContent.Add(method);
    }
}

然后,您将拥有一种特殊的虚拟图定义的类型,该类型覆盖开放/名称方法并在此返回您的动态内容...

之类的东西
public class MethodBasedVirtualFile : VirtualFile
{
    private readonly Func<string> _contentFactory;
    private readonly string _path;
    public MethodBasedVirtualFile(string path, Func<string> contentFactory)
    {
        _path = path;
        _contentFactory = contentFactory;
    }
    public override string Name { get { return _path; } }
    public override Stream Open()
    {
        MemoryStream stream = new MemoryStream();
        StreamWriter writer = new StreamWriter(stream);
        writer.Write(_contentFactory());
        writer.Flush();
        stream.Position = 0;
        return stream;
    }
}

那么,要使用所有您将拥有的...

var bundle = new VirtualMethodBundle();
bundle.Include(... real files ...);
bundle.AddCustomFile(
    new MethodBasedVirtualFile("~/DynamicScript/UrlDictionary",
    ... the method that creates the content of that script...)
);

如果您很聪明,则可以制作一个urlvirtualfile,该urlvirtualfile采用URL路径并使用MVC在需要时自动获取内容。

这是我为这种情况所做的。我定义了一个"捆绑结果",然后用我的控制器方法定义了一个名称。我正在使用ETAG(如果没有匹配的标题)作为客户端缓存的优化。

eg:

public ActionResult ReturnFooBundle()
{
    return new BundleResult("foo", TimeSpan.FromDays(7));
}

这是Bundleresult实现:

public class BundleResult
    : ActionResult
{
    private class BundleInfo
    {
        public string BundleETag;
        public DateTime LastModified;
        public Bundle TheBundle;
        public BundleResponse Response;
    }
    private static Dictionary<string, BundleInfo> _bundleCache = new Dictionary<string, BundleInfo>();
    public string BundleName { get; private set; }
    public TimeSpan CacheExpiry { get; private set; }
    public BundleResult(string bundleName, TimeSpan cacheExpiry)
    {
        BundleName = bundleName;
        CacheExpiry = cacheExpiry;
    }
    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.Clear();
        BundleInfo bundleInfo = GetBundle(context.HttpContext);
        string requestETag = context.HttpContext.Request.Headers["If-None-Match"];
        if (!string.IsNullOrEmpty(requestETag) && (requestETag == bundleInfo.BundleETag))
        {
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotModified;
            context.HttpContext.Response.StatusDescription = "Not Modified";
            return;
        }
        else
        {
            BundleResponse bundleResponse = bundleInfo.Response;
            HttpResponseBase response = context.HttpContext.Response;
            response.Write(bundleResponse.Content);
            response.ContentType = bundleResponse.ContentType;
            HttpCachePolicyBase cache = response.Cache;
            cache.SetCacheability(HttpCacheability.ServerAndPrivate);
            cache.SetLastModified(bundleInfo.LastModified);
            cache.SetETag(bundleInfo.BundleETag);
        }
    }
    private BundleInfo GetBundle(HttpContextBase context)
    {
        // lookup the BundleResponse
        BundleInfo retVal;
        lock (_bundleCache)
        {
            _bundleCache.TryGetValue(BundleName, out retVal);
        }
        if(retVal != null)
        {
#if DEBUG
            // see if the contents have been modified.
            BundleContext bundleContext = new BundleContext(context, BundleTable.Bundles, BundleName);
            DateTime lastModified = retVal.TheBundle.EnumerateFiles(bundleContext).Select(fi => fi.LastWriteTimeUtc).Max();
            if (lastModified > retVal.LastModified)
            {
                // regenerate the bundleInfo
                retVal = null;
            }
#endif
        }
        if (retVal == null)
        {
            string rawBundleName = BundleTable.Bundles.ResolveBundleUrl(BundleName);
            string hash = rawBundleName.Substring(rawBundleName.IndexOf("?v=") + 3);
            Bundle bundle = BundleTable.Bundles.GetBundleFor(BundleName);
            BundleContext bundleContext = new BundleContext(context, BundleTable.Bundles, BundleName);
            BundleResponse bundleResponse = bundle.GenerateBundleResponse(bundleContext);
            DateTime lastModified = bundle.EnumerateFiles(bundleContext).Select(fi => fi.LastWriteTimeUtc).Max();
            retVal = new BundleInfo
            {
                BundleETag = hash,
                Response = bundleResponse,
                TheBundle = bundle,
                LastModified = lastModified,
            };
            lock (_bundleCache)
            {
                _bundleCache[BundleName] = retVal;
            }
        }
        return retVal;
    }
}

捆绑系统仅支持物理文件而不是应用程序路由。

最新更新