0

MVC 4バンドルは、古い.jsファイルの問題を解決しますか?これらの.jsファイルはクライアントのコンピューターにキャッシュされるため、新しい展開で更新されない場合があります。

etagが一致するかどうかをフレームワークにバンドルして認識させることで、MVC 4の問題は解決しますか?

同様に、MVC 3を使用する場合の代替手段は何ですか?

4

3 に答える 3

4

MVC 4バンドリングは、バンドルされたリソースのハッシュを発行します。

例えば。

<link href="@System.Web.Optimization.BundleTable.
    Bundles.ResolveBundleUrl("~/Content/css")"
    rel="stylesheet"
    type="text/css" />

結果は次のようになります。

 <link href="/Content/css?v=ji3nO1pdg6VLv3CVUWntxgZNf1z"
     rel="stylesheet" type="text/css" />

ファイルが変更されると、vパラメータが変更され、クライアントはリソースを再ダウンロードする必要があります。

出典: http: //bit.ly/xT8ZM5

于 2012-11-17T17:46:20.027 に答える
1

MVC3で使用できますSystem.Web.Optimization.dll。ダウンロードして使用できます。

詳細については、http://nuget.org/packages/microsoft.web.optimizationをご覧ください。

たとえば、global.asaxに次を追加します。

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                    "~/Scripts/jquery-ui-{version}.js"));

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.unobtrusive*",
                    "~/Scripts/jquery.validate*"));

bundles.Add(new ScriptBundle("~/bundles/customjs").Include(
                    "~/Scripts/jquery.custom.js"));
// or what you want to add different js files.
于 2012-11-16T15:53:43.453 に答える
1

最近、スクリプトのキャッシュで問題が発生しました。新しいブラウザ(特にChrome)はスクリプトをキャッシュしており、新しいバージョンがあるかどうかを確認するためのリクエストをサーバーに送信しない場合があります。

MVC3アプリでは、カスタムルートハンドラーを使用して処理することにしました。HTMLでは、各スクリプトリンクにリビジョンを追加します。次に、ハンドラーでURLからリビジョン番号を削除し、サーバー上の実際のファイルを検索します(たとえば、Path/Script.rev1000.jsはPath/Script.jsを指します)。

これが私のコードです:

public class ContentRouteHandler : IRouteHandler
{
    private OzirRouteProvider _routeProvider;

    public ContentRouteHandler(OzirRouteProvider routeProvider)
    {
        this._routeProvider = routeProvider;
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new ContentHttpHandler(this._routeProvider, this, requestContext);
    }
}

internal class ContentHttpHandler : IHttpHandler, IRequiresSessionState
{
    private OzirRouteProvider _routeProvider;
    private ContentRouteHandler _routeHandler;
    private RequestContext _requestContext;

    public bool IsReusable { get { return false; } }

    public ContentHttpHandler(OzirRouteProvider routeProvider, ContentRouteHandler routeHandler, RequestContext requestContext)
    {
        this._routeProvider = routeProvider;
        this._routeHandler = routeHandler;
        this._requestContext = requestContext;
    }

    public void ProcessRequest(HttpContext context)
    {
        string contentPath = context.Request.PhysicalPath;
        string fileName = Path.GetFileNameWithoutExtension(contentPath);
        string extension = Path.GetExtension(contentPath);
        string path = Path.GetDirectoryName(contentPath);

        bool minify = false;

        // Here i get fileName like Script.rev1000.min.js
        // I strip revision and .min from it so I'll have Script.js
        var match = Regex.Match(fileName, "(\\.rev\\d+)?(\\.min)?$");
        if (match.Groups[2].Success)
        {
            minify = true;
            fileName = fileName.Remove(match.Groups[2].Index, match.Groups[2].Length);
            contentPath = Path.Combine(path, fileName + extension);
        }
        if (match.Groups[1].Success)
        {
            fileName = fileName.Remove(match.Groups[1].Index, match.Groups[1].Length);
            contentPath = Path.Combine(path, fileName + extension);
        }

        if (!File.Exists(contentPath)) // 404
        {
            throw new HttpException(404, "Not found");
        }

        DateTime lastModified = this.GetModificationDate(contentPath);
        string eTag = this.GetETag(context.Request.RawUrl, contentPath, lastModified);

        // Check for modification
        string requestETag = context.Request.Headers["If-None-Match"];
        string requestLastModified = context.Request.Headers["If-Modified-Since"];
        DateTime? requestLastModifiedDate = requestLastModified == null ? null : (DateTime?)DateTime.Parse(requestLastModified).ToUniversalTime().TruncMiliseconds();

        // Compare e-tag and modification date
        if ((requestLastModified != null || requestETag != null) &&
            (requestLastModified == null || requestLastModifiedDate == lastModified) &&
            (requestETag == null || requestETag == eTag))
        {
            context.Response.StatusCode = 304;
            context.Response.SuppressContent = true;
            context.Response.Flush();
            return;
        }

        switch (extension)
        {
            case ".js":
                context.Response.ContentType = "application/x-javascript";
                if (minify) // minify file?
                {
                    string minContentPath = Path.Combine(path, fileName + ".min" + extension);
                    this.MinifyJs(contentPath, minContentPath);
                    contentPath = minContentPath;
                }
                break;
            default:
                throw new NotSupportedException(string.Format("Extension {0} is not supported yet", extension));
        }

        // g-zip and deflate support
        string acceptEncoding = context.Request.Headers["Accept-Encoding"];
        if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("gzip"))
        {
            context.Response.Filter = new System.IO.Compression.GZipStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress);
            context.Response.AppendHeader("Content-Encoding", "gzip");
        }
        else if (!string.IsNullOrEmpty(acceptEncoding) && acceptEncoding.Contains("deflate"))
        {
            context.Response.Filter = new System.IO.Compression.DeflateStream(context.Response.Filter, System.IO.Compression.CompressionMode.Compress);
            context.Response.AppendHeader("Content-Encoding", "deflate");
        }

        context.Response.AddCacheDependency(new CacheDependency(contentPath));
        context.Response.AddFileDependency(contentPath);
        context.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
        context.Response.Cache.SetETag(eTag);
        context.Response.Cache.SetExpires(DateTime.Now.AddDays(7));
        context.Response.Cache.SetLastModified(lastModified);
        context.Response.Cache.SetMaxAge(TimeSpan.FromDays(7));

        context.Response.TransmitFile(contentPath);
        context.Response.Flush();
    }

    private void MinifyJs(string contentPath, string minContentPath)
    {
        this._log.DebugFormat("Minifying JS {0} into {1}", contentPath, minContentPath);
        if (!File.Exists(minContentPath) || File.GetLastWriteTime(contentPath) > File.GetLastWriteTime(minContentPath))
        {
            string content = File.ReadAllText(contentPath, Encoding.UTF8);

            JavaScriptCompressor compressor = new JavaScriptCompressor();
            compressor.Encoding = Encoding.UTF8;
            compressor.ErrorReporter = new CustomErrorReporter(LoggingType.Debug);

            content = compressor.Compress(content);

            File.WriteAllText(minContentPath, content, Encoding.UTF8);
        }
    }

    private DateTime GetModificationDate(string contentPath)
    {
        DateTime lastModified = File.GetLastWriteTimeUtc(contentPath).TruncMiliseconds();

        return lastModified;
    }

    private string GetETag(string url, string contentPath, DateTime lastModified)
    {
        string eTag = string.Format("url={0},path={1},lm={2},rev={3}", url, contentPath, lastModified, AppInfo.Revision);

        return Quote(GetHash(eTag));
    }

    private static string GetHash(string value)
    {
        byte[] data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(value));

        StringBuilder hex = new StringBuilder(data.Length * 2);
        foreach (byte b in data)
        {
            hex.AppendFormat("{0:x2}", b);
        }
        return hex.ToString();
    }

    private static string Quote(string value)
    {
        return string.Format("\"{0}\"", value);
    }
}

RouteExistingFilesこれを使用するには、ルートをオンにして登録する必要があります。次に例を示します。

routes.Add(new Route("Content/{*resource}", new RouteValueDictionary(), new RouteValueDictionary { { "resource", @".*(\.css)$" } }, contentHandler));
routes.Add(new Route("Scripts/{*resource}", new RouteValueDictionary(), new RouteValueDictionary { { "resource", @".*(\.js)$" } }, contentHandler));
于 2012-11-16T16:23:03.693 に答える