private IReadOnlyList <Endpoint> Update(StaticFileOptionsProvider staticFileOptionsProvider) { const string HtmlExtension = ".html"; const string CatchAllSlugPrefix = "..."; var staticFileOptions = staticFileOptionsProvider.StaticFileOptions; var requestDelegate = CreateRequestDelegate(this.endpointRouteBuilder, staticFileOptions); var endpoints = new List <Endpoint>(); foreach (var filePath in TraverseFiles(staticFileOptions.FileProvider)) { if (!filePath.EndsWith(HtmlExtension)) { continue; } var fileWithoutHtml = filePath.Substring(0, filePath.Length - HtmlExtension.Length); var patternSegments = new List <RoutePatternPathSegment>(); var segments = fileWithoutHtml.Split('/'); // NOTE: Start at 1 because paths here always have a leading slash for (int i = 1; i < segments.Length; i++) { var segment = segments[i]; if (i == segments.Length - 1 && segment == "index") { // Skip `index` segment, match whatever we got so far. // This is so that e.g. file `/a/b/index.html` is served at path `/a/b`, as desired. // TODO: Should we also serve the same file at `/a/b/index`? Note that `/a/b/index.html` will already work // via the UseStaticFiles middleware added by `NextjsStaticHostingExtensions.UseNextjsStaticHosting`. break; } var match = slugRegex.Match(segment); if (match.Success) { string slugName = match.Groups[1].Value; if (slugName.StartsWith(CatchAllSlugPrefix)) { // Catch all route -- see: https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes var parameterName = slugName.Substring(CatchAllSlugPrefix.Length); patternSegments.Add( RoutePatternFactory.Segment( RoutePatternFactory.ParameterPart(parameterName, null, RoutePatternParameterKind.CatchAll))); } else { // Dynamic route -- see: https://nextjs.org/docs/routing/dynamic-routes patternSegments.Add( RoutePatternFactory.Segment( RoutePatternFactory.ParameterPart(slugName))); } } else { // Literal match patternSegments.Add( RoutePatternFactory.Segment( RoutePatternFactory.LiteralPart(segment))); } } var endpointBuilder = new RouteEndpointBuilder(requestDelegate, RoutePatternFactory.Pattern(patternSegments), order: DefaultEndpointOrder); endpointBuilder.Metadata.Add(new StaticFileEndpointMetadata(filePath)); endpointBuilder.DisplayName = $"Next.js {filePath}"; var endpoint = endpointBuilder.Build(); endpoints.Add(endpoint); } return(endpoints); }
internal NextjsEndpointDataSource(IEndpointRouteBuilder endpointRouteBuilder, StaticFileOptionsProvider staticFileOptionsProvider) { this.endpointRouteBuilder = endpointRouteBuilder ?? throw new ArgumentNullException(nameof(endpointRouteBuilder)); _ = staticFileOptionsProvider ?? throw new ArgumentNullException(nameof(staticFileOptionsProvider)); this.endpoints = this.Update(staticFileOptionsProvider); }