Skip to content

Commit e73eee3

Browse files
authored
feat: RPDE cache headers (#163)
1 parent 4d3eabb commit e73eee3

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed
Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,46 @@
1-
namespace BookingSystem.AspNetCore.Helpers
1+
using System;
2+
using System.Threading.Tasks;
3+
using Microsoft.AspNetCore.Http;
4+
using Microsoft.AspNetCore.Mvc;
5+
6+
namespace BookingSystem.AspNetCore.Helpers
27
{
38
public static class ResponseContentHelper
49
{
510
public static Microsoft.AspNetCore.Mvc.ContentResult GetContentResult(this OpenActive.Server.NET.OpenBookingHelper.ResponseContent response)
611
{
7-
return new Microsoft.AspNetCore.Mvc.ContentResult
12+
return new CacheableContentResult
813
{
914
StatusCode = (int)response.StatusCode,
1015
Content = response.Content,
11-
ContentType = response.ContentType
16+
ContentType = response.ContentType,
17+
CacheControlMaxAge = response.CacheControlMaxAge,
1218
};
1319
}
1420
}
21+
22+
/// <summary>
23+
/// ContentResult that also sets Cache-Control: public, max-age=X, s-maxage=X
24+
/// See https://developer.openactive.io/publishing-data/data-feeds/scaling-feeds for more information
25+
/// </summary>
26+
public class CacheableContentResult : Microsoft.AspNetCore.Mvc.ContentResult
27+
{
28+
public TimeSpan CacheControlMaxAge { get; set; }
29+
30+
public override async Task ExecuteResultAsync(ActionContext context)
31+
{
32+
if (CacheControlMaxAge != null)
33+
{
34+
context.HttpContext.Response.GetTypedHeaders().CacheControl =
35+
new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
36+
{
37+
Public = true,
38+
MaxAge = CacheControlMaxAge,
39+
SharedMaxAge = CacheControlMaxAge,
40+
};
41+
}
42+
43+
await base.ExecuteResultAsync(context);
44+
}
45+
}
1546
}

Examples/BookingSystem.AspNetFramework/Helpers/ResponseContentHelper.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ public static HttpResponseMessage GetContentResult(this OpenActive.Server.NET.Op
1414
StatusCode = response.StatusCode
1515
};
1616
resp.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(response.ContentType);
17+
/// Sets additional header Cache-Control: public, max-age=X, s-maxage=X
18+
/// See https://developer.openactive.io/publishing-data/data-feeds/scaling-feeds for more information
19+
if (response.CacheControlMaxAge != null)
20+
{
21+
resp.Headers.CacheControl = new CacheControlHeaderValue()
22+
{
23+
Public = true,
24+
MaxAge = response.CacheControlMaxAge,
25+
SharedMaxAge = response.CacheControlMaxAge
26+
};
27+
}
1728
// Ensure custom error messages do not override responses
1829
HttpContext.Current.Response.TrySkipIisCustomErrors = true;
1930
return resp;

OpenActive.Server.NET/CustomBookingEngine/CustomBookingEngine.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,15 @@ public async Task<ResponseContent> RenderDatasetSite()
169169
/// <returns></returns>
170170
public async Task<ResponseContent> GetOpenDataRPDEPageForFeed(string feedname, string afterTimestamp, string afterId, string afterChangeNumber)
171171
{
172-
return ResponseContent.RpdeResponse(
172+
return GetResponseContentFromRPDEPage(
173173
(await RouteOpenDataRPDEPageForFeed(
174174
feedname,
175175
RpdeOrderingStrategyRouter.ConvertStringToLongOrThrow(afterTimestamp, nameof(afterTimestamp)),
176176
afterId,
177177
RpdeOrderingStrategyRouter.ConvertStringToLongOrThrow(afterChangeNumber, nameof(afterChangeNumber))
178-
)).ToString());
178+
)));
179179
}
180180

181-
182181
/// <summary>
183182
/// Handler for an RPDE endpoint
184183
/// Designed to be used on a single controller method with a "feedname" parameter,
@@ -191,11 +190,14 @@ public async Task<ResponseContent> GetOpenDataRPDEPageForFeed(string feedname, s
191190
/// <returns></returns>
192191
public async Task<ResponseContent> GetOpenDataRPDEPageForFeed(string feedname, long? afterTimestamp, string afterId, long? afterChangeNumber)
193192
{
194-
return ResponseContent.RpdeResponse((await RouteOpenDataRPDEPageForFeed(feedname, afterTimestamp, afterId, afterChangeNumber)).ToString());
193+
return GetResponseContentFromRPDEPage(await RouteOpenDataRPDEPageForFeed(feedname, afterTimestamp, afterId, afterChangeNumber));
195194
}
196195

197-
198-
196+
private ResponseContent GetResponseContentFromRPDEPage(RpdePage rpdePage)
197+
{
198+
var cacheMaxAge = rpdePage.Items.Count == 0 ? this.settings.RPDELastPageCacheDuration : this.settings.RPDEPageCacheDuration;
199+
return ResponseContent.RpdeResponse(rpdePage.ToString(), cacheMaxAge);
200+
}
199201

200202
/// <summary>
201203
/// Handler for an RPDE endpoint

OpenActive.Server.NET/OpenBookingHelper/Content/ResponseContent.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using OpenActive.NET;
2+
using System;
23
using System.Net;
34

45
namespace OpenActive.Server.NET.OpenBookingHelper
@@ -60,13 +61,14 @@ public static ResponseContent OrdersFeedRpdeResponse(string content)
6061
};
6162
}
6263

63-
public static ResponseContent RpdeResponse(string content)
64+
public static ResponseContent RpdeResponse(string content, TimeSpan cacheControlMaxAge)
6465
{
6566
return new ResponseContent
6667
{
6768
Content = content,
6869
ContentType = OpenActiveMediaTypes.RealtimePagedDataExchange.Version1,
69-
StatusCode = HttpStatusCode.OK
70+
StatusCode = HttpStatusCode.OK,
71+
CacheControlMaxAge = cacheControlMaxAge
7072
};
7173
}
7274

@@ -82,6 +84,10 @@ public static ResponseContent RpdeResponse(string content)
8284
// Summary:
8385
// The intended HTTP status code of the response
8486
public HttpStatusCode StatusCode { get; internal set; } = HttpStatusCode.OK;
87+
//
88+
// Summary:
89+
// The Cache-Control header intended for the response (public, max-age=X)
90+
public TimeSpan CacheControlMaxAge { get; internal set; }
8591

8692
public override string ToString()
8793
{

OpenActive.Server.NET/OpenBookingHelper/Settings/BookingEngineSettings.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ public class BookingEngineSettings
2626
public OrdersRPDEFeedGenerator OrdersFeedGenerator { get; set; }
2727
public OrdersRPDEFeedGenerator OrderProposalsFeedGenerator { get; set; }
2828
public SellerStore SellerStore { get; set; }
29-
public bool HasSingleSeller { get; set; }
29+
public bool HasSingleSeller { get; set; } = false;
30+
/// <summary>
31+
/// TTL in the Cache-Control header for all RPDE pages that contain greater than zero items
32+
/// See https://developer.openactive.io/publishing-data/data-feeds/scaling-feeds for CDN configuration instructions
33+
/// </summary>
34+
public TimeSpan RPDEPageCacheDuration { get; set; } = TimeSpan.FromHours(1);
35+
/// <summary>
36+
/// TTL in the Cache-Control header for all RPDE pages that contain zero items
37+
/// See https://developer.openactive.io/publishing-data/data-feeds/scaling-feeds for CDN configuration instructions
38+
/// </summary>
39+
public TimeSpan RPDELastPageCacheDuration { get; set; } = TimeSpan.FromSeconds(8);
3040
}
3141
}

0 commit comments

Comments
 (0)