mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Implement cache priming prototype
This commit is contained in:
44
Microsoft.AspNet.AngularServices/PrimeCacheHelper.cs
Normal file
44
Microsoft.AspNet.AngularServices/PrimeCacheHelper.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.AngularServices {
|
||||||
|
public static class PrimeCacheHelper {
|
||||||
|
public static async Task<HtmlString> PrimeCache(this IHtmlHelper html, string url) {
|
||||||
|
// TODO: Consider deduplicating the PrimeCache calls (that is, if there are multiple requests to precache
|
||||||
|
// the same URL, only return nonempty for one of them). This will make it easier to auto-prime-cache any
|
||||||
|
// HTTP requests made during server-side rendering, without risking unnecessary duplicate requests.
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(url)) {
|
||||||
|
throw new ArgumentException("Value cannot be null or empty", "url");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var request = html.ViewContext.HttpContext.Request;
|
||||||
|
var baseUri = new Uri(string.Concat(request.Scheme, "://", request.Host.ToUriComponent(), request.PathBase.ToUriComponent(), request.Path.ToUriComponent(), request.QueryString.ToUriComponent()));
|
||||||
|
var fullUri = new Uri(baseUri, url);
|
||||||
|
var response = await new HttpClient().GetAsync(fullUri.ToString());
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
return new HtmlString(FormatAsScript(url, response.StatusCode, responseBody));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
var logger = (ILogger)html.ViewContext.HttpContext.ApplicationServices.GetService(typeof (ILogger));
|
||||||
|
if (logger != null) {
|
||||||
|
logger.LogWarning("Error priming cache for URL: " + url, ex);
|
||||||
|
}
|
||||||
|
return new HtmlString(string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatAsScript(string url, HttpStatusCode responseStatusCode, string responseBody)
|
||||||
|
{
|
||||||
|
return string.Format(@"<script>window.__preCachedResponses = window.__preCachedResponses || {{}}; window.__preCachedResponses[{0}] = {1};</script>",
|
||||||
|
JsonConvert.SerializeObject(url),
|
||||||
|
JsonConvert.SerializeObject(new { statusCode = responseStatusCode, body = responseBody })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,8 @@ builder.config({
|
|||||||
'angular2-aspnet/*': 'dist/*'
|
'angular2-aspnet/*': 'dist/*'
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
'angular2/*': { build: false }
|
'angular2/*': { build: false },
|
||||||
|
'@reactivex/*': { build: false }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "angular2-aspnet",
|
"name": "angular2-aspnet",
|
||||||
"version": "0.0.1",
|
"version": "0.0.3",
|
||||||
"description": "Helpers for Angular 2 apps built on ASP.NET",
|
"description": "Helpers for Angular 2 apps built on ASP.NET",
|
||||||
"main": "./dist/Exports",
|
"main": "./dist/Exports",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"author": "Microsoft",
|
"author": "Microsoft",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"angular2": "^2.0.0-alpha.44"
|
"angular2": "2.0.0-alpha.44"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"systemjs-builder": "^0.14.11",
|
"systemjs-builder": "^0.14.11",
|
||||||
|
|||||||
63
Microsoft.AspNet.AngularServices/npm/src/CachePrimedHttp.ts
Normal file
63
Microsoft.AspNet.AngularServices/npm/src/CachePrimedHttp.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { provide, Injectable, Provider } from 'angular2/core';
|
||||||
|
import { Connection, ConnectionBackend, Http, XHRBackend, RequestOptions, Request, RequestMethods, Response, ResponseOptions, ReadyStates } from 'angular2/http';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CachePrimedConnectionBackend extends ConnectionBackend {
|
||||||
|
private _preCachedResponses: PreCachedResponses;
|
||||||
|
|
||||||
|
constructor(private _underlyingBackend: ConnectionBackend, private _baseResponseOptions: ResponseOptions) {
|
||||||
|
super();
|
||||||
|
this._preCachedResponses = (<any>window).__preCachedResponses || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public createConnection(request: Request): Connection {
|
||||||
|
let cacheKey = request.url;
|
||||||
|
if (request.method === RequestMethods.Get && this._preCachedResponses.hasOwnProperty(cacheKey)) {
|
||||||
|
return new CacheHitConnection(request, this._preCachedResponses[cacheKey], this._baseResponseOptions);
|
||||||
|
} else {
|
||||||
|
return this._underlyingBackend.createConnection(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CacheHitConnection implements Connection {
|
||||||
|
readyState: ReadyStates;
|
||||||
|
request: Request;
|
||||||
|
response: any;
|
||||||
|
|
||||||
|
constructor (req: Request, cachedResponse: PreCachedResponse, baseResponseOptions: ResponseOptions) {
|
||||||
|
this.request = req;
|
||||||
|
this.readyState = ReadyStates.Done;
|
||||||
|
|
||||||
|
// Workaround for difficulty consuming CommonJS default exports in TypeScript. Note that it has to be a dynamic
|
||||||
|
// 'require', and not an 'import' statement, because the module isn't available on the server.
|
||||||
|
// All this badness goes away with the next update of Angular 2, as it exposes Observable directly from angular2/core.
|
||||||
|
// --
|
||||||
|
// The current version of Angular exposes the following SystemJS module directly (it is *not* coming from the
|
||||||
|
// @reactivex/rxjs NPM package - it's coming from angular2).
|
||||||
|
let obsCtor: any = require('@reactivex/rxjs/dist/cjs/Observable');
|
||||||
|
this.response = new obsCtor(responseObserver => {
|
||||||
|
let response = new Response(new ResponseOptions({ body: cachedResponse.body, status: cachedResponse.statusCode }));
|
||||||
|
responseObserver.next(response);
|
||||||
|
responseObserver.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var require: any; // Part of the workaround mentioned below. Can remove this after updating Angular.
|
||||||
|
|
||||||
|
interface PreCachedResponses {
|
||||||
|
[url: string]: PreCachedResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PreCachedResponse {
|
||||||
|
statusCode: number;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CACHE_PRIMED_HTTP_PROVIDERS = [
|
||||||
|
provide(Http, {
|
||||||
|
useFactory: (xhrBackend, requestOptions, responseOptions) => new Http(new CachePrimedConnectionBackend(xhrBackend, responseOptions), requestOptions),
|
||||||
|
deps: [XHRBackend, RequestOptions, ResponseOptions]
|
||||||
|
}),
|
||||||
|
];
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
export * from './CachePrimedHttp';
|
||||||
export * from './Validation';
|
export * from './Validation';
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"target": "es5",
|
"target": "es5",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
"noLib": false,
|
"noLib": false,
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"Microsoft.CSharp": "4.0.1-beta-*",
|
"Microsoft.CSharp": "4.0.1-beta-*",
|
||||||
"System.Collections": "4.0.11-beta-*",
|
"System.Collections": "4.0.11-beta-*",
|
||||||
"System.Linq": "4.0.1-beta-*",
|
"System.Linq": "4.0.1-beta-*",
|
||||||
|
"System.Net.Http": "4.0.1-beta-*",
|
||||||
"System.Runtime": "4.0.21-beta-*",
|
"System.Runtime": "4.0.21-beta-*",
|
||||||
"System.Threading": "4.0.11-beta-*"
|
"System.Threading": "4.0.11-beta-*"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user