Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performing JSInterop call during pre-rendering results in a null ref #13396

Closed
pranavkm opened this issue Aug 23, 2019 · 2 comments
Closed

Performing JSInterop call during pre-rendering results in a null ref #13396

pranavkm opened this issue Aug 23, 2019 · 2 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. Done This issue has been fixed

Comments

@pranavkm
Copy link
Contributor

pranavkm commented Aug 23, 2019

@inject IJSRuntime JS
@code {
protected async Task OnInitializedAsync()
{
    await JS.InvokeVoidAsync("toString");
}

Expect: Pretty printed exception message that says you can't do this when the app is pre-rendering

Actual:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSRuntime.BeginInvokeJS(Int64 asyncHandle, String identifier, String argsJson)
   at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](String identifier, CancellationToken cancellationToken, Object[] args)
   at Microsoft.JSInterop.JSRuntime.InvokeWithDefaultCancellation[T](String identifier, Object[] args)
   at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(IJSRuntime jsRuntime, String identifier, Object[] args)
   at BlazorApp2.Pages.Index.OnInitializedAsync() in C:\Users\prkrishn\source\repos\BlazorApp2\BlazorApp2\Pages\Index.razor:line 12
@pranavkm pranavkm added area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. labels Aug 23, 2019
@pranavkm pranavkm added this to the 5.0.0-preview1 milestone Aug 26, 2019
@Joelius300
Copy link

Joelius300 commented Aug 31, 2019

I don't know if there is another issue for this but I get the same exception even when I invoke js from OnAfterRenderAsync but only in a server-side project.
That doesn't seem right.. Shouldn't the pre-rendering already be done there?

I have a reusable component in a razor library:

@using Microsoft.JSInterop
@inject IJSRuntime jsRuntime

<p>This is a js-interop test component.</p>

@code{
    protected override async Task OnAfterRenderAsync()
    {
        string input = await jsRuntime.InvokeAsync<string>(
                "exampleJsFunctions.showPrompt",
                "Type something");

        Console.WriteLine(input);
    }
}

The js-code is just this:

window.exampleJsFunctions = {
  showPrompt: function (message) {
    return prompt(message, 'Type anything here');
  }
};

Consumed by a client side blazor application (without backend), everything works as expected.
However, when used in a server side blazor application, it results in the following exception.

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSRuntime.BeginInvokeJS(Int64 asyncHandle, String identifier, String argsJson)
   at Microsoft.JSInterop.JSRuntimeBase.InvokeAsync[T](String identifier, IEnumerable`1 args, CancellationToken cancellationToken)
   at Microsoft.JSInterop.JSRuntimeBase.InvokeWithDefaultCancellation[T](String identifier, IEnumerable`1 args)
   at SampleLibrary.Component1.OnAfterRenderAsync() in C:\StaticAssetsTest\SampleLibrary\Component1.razor:line 9
   at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
   at Microsoft.AspNetCore.Components.Rendering.Renderer.AddToPendingTasks(Task task)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
   at Microsoft.AspNetCore.Components.Rendering.Renderer.RenderRootComponentAsync(Int32 componentId, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.CreateInitialRenderAsync(Type componentType, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.RenderComponentAsync(Type componentType, ParameterView initialParameters)
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c__11`1.<<InvokeAsync>b__11_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.ViewFeatures.RazorComponents.StaticComponentRenderer.PrerenderComponentAsync(ParameterView parameters, HttpContext httpContext, Type componentType)
   at Microsoft.AspNetCore.Mvc.Rendering.HtmlHelperRazorComponentExtensions.RenderStaticComponentAsync[TComponent](IHtmlHelper htmlHelper, Object parameters)
   at WebCore_SS.Pages.Pages__Host.<ExecuteAsync>b__10_1() in C:\StaticAssetsTest\WebCore_SS\Pages\_Host.cshtml:line 18
   at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
   at WebCore_SS.Pages.Pages__Host.ExecuteAsync()
   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)
   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.SetRoutingAndContinue(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

This always occures when directly going to the page this component is on. If you navigate there later using a NavLink it won't occur.

FYI I have also noticed the exact same behaviour when using OnInitializedAsync, CS works and SS doesn't.

So there are two issues:

  1. Same as the orignal issue here, it doesn't show a user-friendly exception.
  2. It happens even when using AfterRender which I would assume is executed after any (pre-)rendering is done.

You can apparently work around this error if you use ContinueWith on a one ms delay like so:

await Task.Delay(1).ContinueWith(async T =>
        {
            string input = await jsRuntime.InvokeAsync<string>(
                    "exampleJsFunctions.showPrompt",
                    "Type something");

            Console.WriteLine(input);
        });

This workaround doesn't affect client side although you can already see the rendered content before the prompt pops up. In server side, the behaviour is now the same as in client side.

@peteryang023
Copy link

Another option is to check if the client connection is already established, like below shows:

@page "/"
@using Microsoft.JSInterop
@inject IComponentContext ComponentContext
@inject IJSRuntime jsRuntime

<p>This is a js-interop test component.</p>

@code{

    protected override async Task OnAfterRenderAsync()
    {
        if (!ComponentContext.IsConnected) return;

        string input = await jsRuntime.InvokeAsync<string>(
                "exampleJsFunctions.showPrompt",
                "Type something");

        Console.WriteLine(input);
    }
}

You can refer to https://docs.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-3.0#server-side for the detail.

pranavkm added a commit that referenced this issue Oct 1, 2019
pranavkm added a commit that referenced this issue Oct 1, 2019
mkArtakMSFT pushed a commit that referenced this issue Oct 1, 2019
@pranavkm pranavkm closed this as completed Oct 2, 2019
@mkArtakMSFT mkArtakMSFT added Done This issue has been fixed and removed Working labels Oct 3, 2019
@ghost ghost locked as resolved and limited conversation to collaborators Dec 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. Done This issue has been fixed
Projects
None yet
Development

No branches or pull requests

4 participants