// GET<delimiter>SCHEME<delimiter>HOST:PORT/PATHBASE/PATH<delimiter>H<delimiter>HeaderName=HeaderValue<delimiter>Q<delimiter>QueryName=QueryValue1<subdelimiter>QueryValue2<delimiter>R<delimiter>RouteName1=RouteValue1<subdelimiter>RouteName2=RouteValue2 public string CreateStorageKey(OutputCacheContext context) { ArgumentNullException.ThrowIfNull(_builderPool); var varyByRules = context.CacheVaryByRules; if (varyByRules == null) { throw new InvalidOperationException($"{nameof(CacheVaryByRules)} must not be null on the {nameof(OutputCacheContext)}"); } var request = context.HttpContext.Request; var builder = _builderPool.Get(); try { builder .AppendUpperInvariant(request.Method) .Append(KeyDelimiter) .AppendUpperInvariant(request.Scheme) .Append(KeyDelimiter) .AppendUpperInvariant(request.Host.Value); if (_options.UseCaseSensitivePaths) { builder .Append(request.PathBase.Value) .Append(request.Path.Value); } else { builder .AppendUpperInvariant(request.PathBase.Value) .AppendUpperInvariant(request.Path.Value); } // Vary by prefix and custom var prefixCount = varyByRules?.VaryByPrefix.Count ?? 0; if (prefixCount > 0) { // Append a group separator for the header segment of the cache key builder.Append(KeyDelimiter) .Append('C'); for (var i = 0; i < prefixCount; i++) { var value = varyByRules?.VaryByPrefix[i] ?? string.Empty; builder.Append(KeyDelimiter).Append(value); } } // Vary by header names var headersCount = varyByRules?.HeaderNames.Count ?? 0; if (headersCount > 0) { // Append a group separator for the header segment of the cache key builder.Append(KeyDelimiter) .Append('H'); var requestHeaders = context.HttpContext.Request.Headers; for (var i = 0; i < headersCount; i++) { var header = varyByRules !.HeaderNames[i] ?? string.Empty; var headerValues = requestHeaders[header]; builder.Append(KeyDelimiter) .Append(header) .Append('='); var headerValuesArray = headerValues.ToArray(); Array.Sort(headerValuesArray, StringComparer.Ordinal); for (var j = 0; j < headerValuesArray.Length; j++) { builder.Append(headerValuesArray[j]); } } } // Vary by query keys if (varyByRules?.QueryKeys.Count > 0) { // Append a group separator for the query key segment of the cache key builder.Append(KeyDelimiter) .Append('Q'); if (varyByRules.QueryKeys.Count == 1 && string.Equals(varyByRules.QueryKeys[0], "*", StringComparison.Ordinal) && context.HttpContext.Request.Query.Count > 0) { // Vary by all available query keys var queryArray = context.HttpContext.Request.Query.ToArray(); // Query keys are aggregated case-insensitively whereas the query values are compared ordinally. Array.Sort(queryArray, QueryKeyComparer.OrdinalIgnoreCase); for (var i = 0; i < queryArray.Length; i++) { builder.Append(KeyDelimiter) .AppendUpperInvariant(queryArray[i].Key) .Append('='); var queryValueArray = queryArray[i].Value.ToArray(); Array.Sort(queryValueArray, StringComparer.Ordinal); for (var j = 0; j < queryValueArray.Length; j++) { if (j > 0) { builder.Append(KeySubDelimiter); } builder.Append(queryValueArray[j]); } } } else { for (var i = 0; i < varyByRules.QueryKeys.Count; i++) { var queryKey = varyByRules.QueryKeys[i] ?? string.Empty; var queryKeyValues = context.HttpContext.Request.Query[queryKey]; builder.Append(KeyDelimiter) .Append(queryKey) .Append('='); var queryValueArray = queryKeyValues.ToArray(); Array.Sort(queryValueArray, StringComparer.Ordinal); for (var j = 0; j < queryValueArray.Length; j++) { if (j > 0) { builder.Append(KeySubDelimiter); } builder.Append(queryValueArray[j]); } } } } // Vary by route value names var routeValueNamesCount = varyByRules?.RouteValueNames.Count ?? 0; if (routeValueNamesCount > 0) { // Append a group separator for the route values segment of the cache key builder.Append(KeyDelimiter) .Append('R'); for (var i = 0; i < routeValueNamesCount; i++) { // The lookup key can't be null var routeValueName = varyByRules !.RouteValueNames[i] ?? string.Empty; // RouteValueNames returns null if the key doesn't exist var routeValueValue = context.HttpContext.Request.RouteValues[routeValueName]; builder.Append(KeyDelimiter) .Append(routeValueName) .Append('=') .Append(Convert.ToString(routeValueValue, CultureInfo.InvariantCulture)); } } return(builder.ToString()); } finally { _builderPool.Return(builder); } }
/// <inheritdoc /> ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken) { return(ValueTask.CompletedTask); }