Browse Source

Etag improvements.

pull/320/head
Sebastian Stehle 7 years ago
parent
commit
8bbf4c9b0e
  1. 3
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  2. 3
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  3. 18
      src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  4. 5
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs
  5. 8
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs
  6. 16
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  7. 5
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  8. 11
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  9. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  10. 2
      src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  11. 12
      src/Squidex/Pipeline/ETagFilter.cs
  12. 31
      src/Squidex/Pipeline/EtagExtensions.cs
  13. 6
      src/Squidex/Pipeline/IGenerateEtag.cs

3
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -14,7 +14,6 @@ using Squidex.Areas.Api.Controllers.Apps.Models;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Security;
using Squidex.Pipeline;
@ -63,7 +62,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
var response = entities.Select(a => AppDto.FromApp(a, subject, appPlansProvider)).ToList();
Response.Headers["Etag"] = string.Join(";", response.Select(x => $"{x.Id}{x.Version}")).Sha256Base64();
Response.Headers["ETag"] = response.ToManyEtag();
return Ok(response);
}

3
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -14,10 +14,11 @@ using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
public sealed class AppDto
public sealed class AppDto : IGenerateEtag
{
/// <summary>
/// The name of the app.

18
src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs

@ -7,13 +7,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using NSwag.Annotations;
using Squidex.Areas.Api.Controllers.Assets.Models;
using Squidex.Areas.Api.Controllers.Contents;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps.Services;
@ -39,6 +39,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
private readonly IAssetQueryService assetQuery;
private readonly IAssetStatsRepository assetStatsRepository;
private readonly IAppPlansProvider appPlanProvider;
private readonly IOptions<MyContentsControllerOptions> controllerOptions;
private readonly ITagService tagService;
private readonly AssetConfig assetsConfig;
@ -48,6 +49,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
IAssetStatsRepository assetStatsRepository,
IAppPlansProvider appPlanProvider,
IOptions<AssetConfig> assetsConfig,
IOptions<MyContentsControllerOptions> controllerOptions,
ITagService tagService)
: base(commandBus)
{
@ -55,6 +57,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
this.assetQuery = assetQuery;
this.assetStatsRepository = assetStatsRepository;
this.appPlanProvider = appPlanProvider;
this.controllerOptions = controllerOptions;
this.tagService = tagService;
}
@ -106,7 +109,12 @@ namespace Squidex.Areas.Api.Controllers.Assets
var response = AssetsDto.FromAssets(assets);
Response.Headers["Surrogate-Key"] = string.Join(" ", response.Items.Select(x => x.Id));
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = response.Items.ToSurrogateKeys();
}
Response.Headers["ETag"] = response.Items.ToManyEtag(response.Total);
return Ok(response);
}
@ -138,8 +146,12 @@ namespace Squidex.Areas.Api.Controllers.Assets
var response = AssetDto.FromAsset(entity);
if (controllerOptions.Value.EnableSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = entity.Id.ToString();
}
Response.Headers["ETag"] = entity.Version.ToString();
Response.Headers["Surrogate-Key"] = entity.Id.ToString();
return Ok(response);
}

5
src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs

@ -98,11 +98,6 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
/// </summary>
public long Version { get; set; }
public string GenerateETag()
{
return $"{Id}{Version}";
}
public static AssetDto FromAsset(IAssetEntity asset)
{
return SimpleMapper.Map(asset, new AssetDto { FileType = asset.FileName.FileType() });

8
src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs

@ -9,11 +9,10 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Assets.Models
{
public sealed class AssetsDto : IGenerateEtag
public sealed class AssetsDto
{
/// <summary>
/// The assets.
@ -26,11 +25,6 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
/// </summary>
public long Total { get; set; }
public string GenerateETag()
{
return string.Join(";", Items?.Select(x => x.GenerateETag()) ?? Enumerable.Empty<string>()).Sha256Base64();
}
public static AssetsDto FromAssets(IResultList<IAssetEntity> assets)
{
return new AssetsDto { Total = assets.Total, Items = assets.Select(AssetDto.FromAsset).ToArray() };

16
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -138,13 +138,13 @@ namespace Squidex.Areas.Api.Controllers.Contents
Items = result.Take(200).Select(x => ContentDto.FromContent(x, context.Base)).ToArray()
};
var options = controllerOptions.Value;
if (options.EnableSurrogateKeys && response.Items.Length <= options.MaxItemsForSurrogateKeys)
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = string.Join(" ", response.Items.Select(x => x.Id));
Response.Headers["Surrogate-Key"] = response.Items.ToSurrogateKeys();
}
Response.Headers["ETag"] = response.Items.ToManyEtag(response.Total);
return Ok(response);
}
@ -172,13 +172,13 @@ namespace Squidex.Areas.Api.Controllers.Contents
var response = ContentDto.FromContent(content, context.Base);
Response.Headers["ETag"] = content.Version.ToString();
if (controllerOptions.Value.EnableSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = content.Id.ToString();
}
Response.Headers["ETag"] = content.Version.ToString();
return Ok(response);
}
@ -208,13 +208,13 @@ namespace Squidex.Areas.Api.Controllers.Contents
var response = ContentDto.FromContent(content, context.Base);
Response.Headers["ETag"] = content.Version.ToString();
if (controllerOptions.Value.EnableSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = content.Id.ToString();
}
Response.Headers["ETag"] = content.Version.ToString();
return Ok(response.Data);
}

5
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -80,11 +80,6 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// </summary>
public long Version { get; set; }
public string GenerateETag()
{
return $"{Id}{Version}";
}
public static ContentDto FromCommand(CreateContent command, EntityCreatedResult<NamedContentData> result)
{
var now = SystemClock.Instance.GetCurrentInstant();

11
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs

@ -5,13 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Linq;
using Squidex.Infrastructure;
using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Contents.Models
{
public sealed class ContentsDto : IGenerateEtag
public sealed class ContentsDto
{
/// <summary>
/// The total number of content items.
@ -22,10 +18,5 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// The content items.
/// </summary>
public ContentDto[] Items { get; set; }
public string GenerateETag()
{
return string.Join(";", Items?.Select(x => x.GenerateETag()) ?? Enumerable.Empty<string>()).Sha256Base64();
}
}
}

5
src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -11,10 +11,11 @@ using NodaTime;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
public sealed class SchemaDto
public sealed class SchemaDto : IGenerateEtag
{
/// <summary>
/// The id of the schema.
@ -74,7 +75,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// <summary>
/// The version of the schema.
/// </summary>
public int Version { get; set; }
public long Version { get; set; }
public static SchemaDto FromSchema(ISchemaEntity schema)
{

2
src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -55,6 +55,8 @@ namespace Squidex.Areas.Api.Controllers.Schemas
var response = schemas.Select(SchemaDto.FromSchema).ToList();
Response.Headers["ETag"] = response.ToManyEtag();
return Ok(response);
}

12
src/Squidex/Pipeline/ETagFilter.cs

@ -19,20 +19,10 @@ namespace Squidex.Pipeline
var httpContext = context.HttpContext;
if (!httpContext.Response.Headers.TryGetValue("Etag", out _) && resultContext.Result is ObjectResult obj && obj.Value is IGenerateEtag g)
{
var calculatedEtag = g.GenerateETag();
if (!string.IsNullOrWhiteSpace(calculatedEtag))
{
httpContext.Response.Headers.Add("Etag", calculatedEtag);
}
}
if (httpContext.Request.Method == "GET" &&
httpContext.Request.Headers.TryGetValue("If-None-Match", out var noneMatch) &&
httpContext.Response.StatusCode == 200 &&
httpContext.Response.Headers.TryGetValue("Etag", out var etag) &&
httpContext.Response.Headers.TryGetValue("ETag", out var etag) &&
!string.IsNullOrWhiteSpace(noneMatch) &&
!string.IsNullOrWhiteSpace(etag) &&
string.Equals(etag, noneMatch, System.StringComparison.Ordinal))

31
src/Squidex/Pipeline/EtagExtensions.cs

@ -0,0 +1,31 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure;
namespace Squidex.Pipeline
{
public static class EtagExtensions
{
public static string ToManyEtag<T>(this IEnumerable<T> items, long total = 0) where T : IGenerateEtag
{
return $"{total}_{string.Join(";", items.Select(x => $"{x.Id}{x.Version}"))}".Sha256Base64();
}
public static string ToSurrogateKeys<T>(this IEnumerable<T> items) where T : IGenerateEtag
{
return string.Join(" ", items.Select(x => x.Id));
}
public static string ToEtag<T>(this T item) where T : IGenerateEtag
{
return item.Version.ToString();
}
}
}

6
src/Squidex/Pipeline/IGenerateEtag.cs

@ -5,10 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Pipeline
{
public interface IGenerateEtag
{
string GenerateETag();
Guid Id { get; }
long Version { get; }
}
}

Loading…
Cancel
Save