コード例 #1
0
        public async Task ReplacesDashesWithDots_WhenTheyAppearInPairs()
        {
            // Arrange
            var circuitFactory  = new TestCircuitFactory(() => "--1234--");
            var circuitRegistry = new CircuitRegistry(
                Options.Create(new CircuitOptions()),
                Mock.Of <ILogger <CircuitRegistry> >(),
                TestCircuitIdFactory.CreateTestFactory());
            var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry);
            var httpContext        = new DefaultHttpContext();
            var httpRequest        = httpContext.Request;

            httpRequest.Scheme = "https";
            httpRequest.Host   = new HostString("example.com", 1234);
            httpRequest.Path   = "/some/path";

            var prerenderingContext = new ComponentPrerenderingContext
            {
                ComponentType = typeof(UriDisplayComponent),
                Parameters    = ParameterView.Empty,
                Context       = httpContext
            };

            // Act
            var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext);

            // Assert
            Assert.Equal("..1234..", GetUnwrappedCircuitInfo(result).RootElement.GetProperty("circuitId").GetString());
        }
コード例 #2
0
        public async Task <IEnumerable <string> > PrerenderComponentAsync(ComponentPrerenderingContext prerenderingContext)
        {
            var context     = prerenderingContext.Context;
            var circuitHost = _circuitFactory.CreateCircuitHost(
                context,
                client: CircuitClientProxy.OfflineClient,
                GetFullUri(context.Request),
                GetFullBaseUri(context.Request));

            // We don't need to unsubscribe because the circuit host object is scoped to this call.
            circuitHost.UnhandledException += CircuitHost_UnhandledException;

            // For right now we just do prerendering and dispose the circuit. In the future we will keep the circuit around and
            // reconnect to it from the ComponentsHub. If we keep the circuit/renderer we also need to unsubscribe this error
            // handler.
            try
            {
                return(await circuitHost.PrerenderComponentAsync(
                           prerenderingContext.ComponentType,
                           prerenderingContext.Parameters));
            }
            finally
            {
                await circuitHost.DisposeAsync();
            }
        }
コード例 #3
0
        public async Task ExtractsUriFromHttpContext_NonemptyPathBase()
        {
            // Arrange
            var circuitFactory     = new TestCircuitFactory();
            var circuitRegistry    = new CircuitRegistry(Options.Create(new CircuitOptions()), Mock.Of <ILogger <CircuitRegistry> >());
            var circuitPrerenderer = new CircuitPrerenderer(circuitFactory, circuitRegistry);
            var httpContext        = new DefaultHttpContext();
            var httpRequest        = httpContext.Request;

            httpRequest.Scheme   = "https";
            httpRequest.Host     = new HostString("example.com", 1234);
            httpRequest.PathBase = "/my/dir";
            httpRequest.Path     = "/some/path";

            var prerenderingContext = new ComponentPrerenderingContext
            {
                ComponentType = typeof(UriDisplayComponent),
                Parameters    = ParameterCollection.Empty,
                Context       = httpContext
            };

            // Act
            var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext);

            // Assert
            Assert.Equal(string.Join("", new[]
            {
                "The current URI is ",
                "https://example.com:1234/my/dir/some/path",
                " within base URI ",
                "https://example.com:1234/my/dir/"
            }), GetUnwrappedContent(result));
        }
コード例 #4
0
        public async Task ExtractsUriFromHttpContext_NonemptyPathBase()
        {
            // Arrange
            var circuitFactory     = new TestCircuitFactory();
            var circuitPrerenderer = new CircuitPrerenderer(circuitFactory);
            var httpContext        = new Mock <HttpContext>();
            var httpRequest        = new Mock <HttpRequest>().SetupAllProperties();

            httpContext.Setup(h => h.Request).Returns(httpRequest.Object);
            httpRequest.Object.Scheme   = "https";
            httpRequest.Object.Host     = new HostString("example.com", 1234);
            httpRequest.Object.PathBase = "/my/dir";
            httpRequest.Object.Path     = "/some/path";

            var prerenderingContext = new ComponentPrerenderingContext
            {
                ComponentType = typeof(UriDisplayComponent),
                Parameters    = ParameterCollection.Empty,
                Context       = httpContext.Object
            };

            // Act
            var result = await circuitPrerenderer.PrerenderComponentAsync(prerenderingContext);

            // Assert
            Assert.Equal(new[]
            {
                "The current URI is ",
                "https://example.com:1234/my/dir/some/path",
                " within base URI ",
                "https://example.com:1234/my/dir/"
            }, result);
        }
コード例 #5
0
        public async Task <ComponentPrerenderResult> PrerenderComponentAsync(ComponentPrerenderingContext prerenderingContext)
        {
            var context          = prerenderingContext.Context;
            var navigationStatus = GetOrCreateNavigationStatus(context);

            if (navigationStatus.Navigated)
            {
                // Avoid creating a circuit host if other component earlier in the pipeline already triggered
                // a navigation request. Instead rendre nothing
                return(new ComponentPrerenderResult(Array.Empty <string>()));
            }
            var circuitHost = GetOrCreateCircuitHost(context, navigationStatus);
            ComponentRenderedText renderResult = default;

            try
            {
                renderResult = await circuitHost.PrerenderComponentAsync(
                    prerenderingContext.ComponentType,
                    prerenderingContext.Parameters);
            }
            catch (NavigationException navigationException)
            {
                // Cleanup the state as we won't need it any longer.
                // Signal callbacks that we don't have to register the circuit.
                await CleanupCircuitState(context, navigationStatus, circuitHost);

                // Navigation was attempted during prerendering.
                if (prerenderingContext.Context.Response.HasStarted)
                {
                    // We can't perform a redirect as the server already started sending the response.
                    // This is considered an application error as the developer should buffer the response until
                    // all components have rendered.
                    throw new InvalidOperationException("A navigation command was attempted during prerendering after the server already started sending the response. " +
                                                        "Navigation commands can not be issued during server-side prerendering after the response from the server has started. Applications must buffer the" +
                                                        "reponse and avoid using features like FlushAsync() before all components on the page have been rendered to prevent failed navigation commands.", navigationException);
                }

                context.Response.Redirect(navigationException.Location);
                return(new ComponentPrerenderResult(Array.Empty <string>()));
            }

            circuitHost.Descriptors.Add(new ComponentDescriptor
            {
                ComponentType = prerenderingContext.ComponentType,
                Prerendered   = true
            });

            var result = (new[] {
                $"<!-- M.A.C.Component:{{\"circuitId\":\"{circuitHost.CircuitId}\",\"rendererId\":\"{circuitHost.Renderer.Id}\",\"componentId\":\"{renderResult.ComponentId}\"}} -->",
            }).Concat(renderResult.Tokens).Concat(
                new[] {
                $"<!-- M.A.C.Component: {renderResult.ComponentId} -->"
            });

            return(new ComponentPrerenderResult(result));
        }
コード例 #6
0
        public async Task <IEnumerable <string> > PrerenderComponentAsync(ComponentPrerenderingContext context)
        {
            var dispatcher = Renderer.CreateDefaultDispatcher();
            var parameters = context.Parameters;

            // This shouldn't be moved to the constructor as we want a request scoped service.
            var helper = (HttpUriHelper)context.Context.RequestServices.GetRequiredService <IUriHelper>();

            helper.InitializeState(context.Context);
            using (var htmlRenderer = new HtmlRenderer(context.Context.RequestServices, _encoder.Encode, dispatcher))
            {
                return(await dispatcher.InvokeAsync(() => htmlRenderer.RenderComponentAsync(
                                                        context.ComponentType,
                                                        parameters)));
            }
        }
コード例 #7
0
        public async Task <IEnumerable <string> > PrerenderComponentAsync(ComponentPrerenderingContext prerenderingContext)
        {
            var context     = prerenderingContext.Context;
            var circuitHost = _circuitFactory.CreateCircuitHost(
                context,
                client: null,
                GetFullUri(context.Request),
                GetFullBaseUri(context.Request));

            // For right now we just do prerendering and dispose the circuit. In the future we will keep the circuit around and
            // reconnect to it from the ComponentsHub.
            try
            {
                return(await circuitHost.PrerenderComponentAsync(
                           prerenderingContext.ComponentType,
                           prerenderingContext.Parameters));
            }
            finally
            {
                await circuitHost.DisposeAsync();
            }
        }
コード例 #8
0
        public async Task DisposesCircuitScopeEvenIfPrerenderingThrows()
        {
            // Arrange
            var circuitFactory  = new MockServiceScopeCircuitFactory();
            var circuitRegistry = new CircuitRegistry(
                Options.Create(new CircuitOptions()),
                Mock.Of <ILogger <CircuitRegistry> >(),
                TestCircuitIdFactory.CreateTestFactory());
            var httpContext         = new DefaultHttpContext();
            var prerenderer         = new CircuitPrerenderer(circuitFactory, circuitRegistry);
            var prerenderingContext = new ComponentPrerenderingContext
            {
                ComponentType = typeof(ThrowExceptionComponent),
                Parameters    = ParameterCollection.Empty,
                Context       = httpContext
            };

            // Act
            await Assert.ThrowsAsync <InvalidTimeZoneException>(async() =>
                                                                await prerenderer.PrerenderComponentAsync(prerenderingContext));

            // Assert
            circuitFactory.MockServiceScope.Verify(scope => scope.Dispose(), Times.Once());
        }
コード例 #9
0
        public async Task <ComponentPrerenderResult> PrerenderComponentAsync(ComponentPrerenderingContext prerenderingContext)
        {
            var context     = prerenderingContext.Context;
            var circuitHost = GetOrCreateCircuitHost(context);

            var renderResult = await circuitHost.PrerenderComponentAsync(
                prerenderingContext.ComponentType,
                prerenderingContext.Parameters);

            circuitHost.Descriptors.Add(new ComponentDescriptor
            {
                ComponentType = prerenderingContext.ComponentType,
                Prerendered   = true
            });

            var result = new[] {
                $"<!-- M.A.C.Component:{{\"circuitId\":\"{circuitHost.CircuitId}\",\"rendererId\":\"{circuitHost.Renderer.Id}\",\"componentId\":\"{renderResult.ComponentId}\"}} -->",
            }.Concat(renderResult.Tokens).Concat(
                new[] {
                $"<!-- M.A.C.Component: {renderResult.ComponentId} -->"
            });

            return(new ComponentPrerenderResult(result));
        }
コード例 #10
0
        public async Task <ComponentPrerenderResult> PrerenderComponentAsync(ComponentPrerenderingContext prerenderingContext)
        {
            var context            = prerenderingContext.Context;
            var cancellationStatus = GetOrCreateCancellationStatus(context);

            if (cancellationStatus.Canceled)
            {
                // Avoid creating a circuit host if other component earlier in the pipeline already triggered
                // cancellation (e.g., by navigating or throwing). Instead render nothing.
                return(new ComponentPrerenderResult(Array.Empty <string>()));
            }
            var circuitHost = GetOrCreateCircuitHost(context, cancellationStatus);
            ComponentRenderedText renderResult;

            try
            {
                renderResult = await circuitHost.PrerenderComponentAsync(
                    prerenderingContext.ComponentType,
                    prerenderingContext.Parameters);
            }
            catch (NavigationException navigationException)
            {
                // Cleanup the state as we won't need it any longer.
                // Signal callbacks that we don't have to register the circuit.
                await CleanupCircuitState(context, cancellationStatus, circuitHost);

                // Navigation was attempted during prerendering.
                if (prerenderingContext.Context.Response.HasStarted)
                {
                    // We can't perform a redirect as the server already started sending the response.
                    // This is considered an application error as the developer should buffer the response until
                    // all components have rendered.
                    throw new InvalidOperationException("A navigation command was attempted during prerendering after the server already started sending the response. " +
                                                        "Navigation commands can not be issued during server-side prerendering after the response from the server has started. Applications must buffer the" +
                                                        "reponse and avoid using features like FlushAsync() before all components on the page have been rendered to prevent failed navigation commands.", navigationException);
                }

                context.Response.Redirect(navigationException.Location);
                return(new ComponentPrerenderResult(Array.Empty <string>()));
            }
            catch
            {
                // If prerendering any component fails, cancel prerendering entirely and dispose the DI scope
                await CleanupCircuitState(context, cancellationStatus, circuitHost);

                throw;
            }

            circuitHost.Descriptors.Add(new ComponentDescriptor
            {
                ComponentType = prerenderingContext.ComponentType,
                Prerendered   = true
            });

            var record = JsonSerializer.Serialize(new PrerenderedComponentRecord(
                                                      // We need to do this due to the fact that -- is not allowed within HTML comments and HTML doesn't encode '-'.
                                                      // We will never have '..' sequences because we Base64UrlEncode the circuit id
                                                      circuitHost.CircuitId.Replace("--", ".."),
                                                      circuitHost.Renderer.Id,
                                                      renderResult.ComponentId),
                                                  _jsonSerializationOptions);

            var result = (new[] {
                $"<!-- M.A.C.Component: {record} -->",
            }).Concat(renderResult.Tokens).Concat(
                new[] {
                $"<!-- M.A.C.Component: {renderResult.ComponentId} -->"
            });

            return(new ComponentPrerenderResult(result));
        }