dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.39k stars 10k forks source link

Can't pass `IJSObjectReference` in calls from JS to .NET #26049

Closed JinShil closed 1 year ago

JinShil commented 4 years ago

Trying to return a DOM Event object as an IJSObjectReference throws an exception:

image

  1. What is an "Unexcepted JSON property"? Did you mean "Unexpected"?
  2. I expect the DOM event object to be passed as an IJSObjectReference to the JSInvokable OnClick function below.

To Reproduce

Bug.csproj

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
      <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0-rc.2.20460.25" />
      <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0-rc.2.20460.25" PrivateAssets="all" />
      <PackageReference Include="System.Net.Http.Json" Version="6.0.0-alpha.1.20420.3" />
  </ItemGroup>

</Project>

Javascript to add to index.html

window.interop = {
    AddEventListener(element, eventName, dotNetObj, dotNetMethod) {
        element.addEventListener(eventName, (e) => {
            dotNetObj.invokeMethod(dotNetMethod, e);
        });
    }
}

Index.razor

@page "/"

@inject IJSRuntime JS

<div @ref="_element" style="width: 200px; height: 200px; background-color: blue;"></div>

@code {
    ElementReference _element;

    [JSInvokable]
    public void OnClick(IJSObjectReference jsReference)
    {
        Console.WriteLine("Clicked");
    }

    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);

        JS.InvokeVoidAsync("interop.AddEventListener", _element, "click", DotNetObjectReference.Create(this), nameof(OnClick));
    }
}
  1. Create a Blazor WASM project with the code above.
  2. Run it
  3. Click on the blue rectangle

Further technical details

dotnet --version
5.0.100-rc.1.20454.5
 dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.100-rc.1.20454.5
 Commit:    53a032d031

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19041
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.100-rc.1.20454.5\

Host (useful for support):
  Version: 5.0.0-rc.1.20451.14
  Commit:  38017c3935

.NET SDKs installed:
  2.1.4 [C:\Program Files\dotnet\sdk]
  2.1.100 [C:\Program Files\dotnet\sdk]
  2.1.101 [C:\Program Files\dotnet\sdk]
  2.1.102 [C:\Program Files\dotnet\sdk]
  2.1.103 [C:\Program Files\dotnet\sdk]
  2.1.104 [C:\Program Files\dotnet\sdk]
  2.1.201 [C:\Program Files\dotnet\sdk]
  2.1.202 [C:\Program Files\dotnet\sdk]
  2.1.301 [C:\Program Files\dotnet\sdk]
  2.1.400 [C:\Program Files\dotnet\sdk]
  2.1.401 [C:\Program Files\dotnet\sdk]
  2.1.402 [C:\Program Files\dotnet\sdk]
  2.1.403 [C:\Program Files\dotnet\sdk]
  2.1.500 [C:\Program Files\dotnet\sdk]
  2.1.502 [C:\Program Files\dotnet\sdk]
  2.1.503 [C:\Program Files\dotnet\sdk]
  2.1.504 [C:\Program Files\dotnet\sdk]
  2.1.505 [C:\Program Files\dotnet\sdk]
  2.1.507 [C:\Program Files\dotnet\sdk]
  2.1.600 [C:\Program Files\dotnet\sdk]
  2.1.601 [C:\Program Files\dotnet\sdk]
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.1.604 [C:\Program Files\dotnet\sdk]
  2.1.700 [C:\Program Files\dotnet\sdk]
  2.1.801 [C:\Program Files\dotnet\sdk]
  2.2.103 [C:\Program Files\dotnet\sdk]
  3.0.100-preview9-014004 [C:\Program Files\dotnet\sdk]
  3.0.100 [C:\Program Files\dotnet\sdk]
  3.1.100 [C:\Program Files\dotnet\sdk]
  3.1.200 [C:\Program Files\dotnet\sdk]
  3.1.301 [C:\Program Files\dotnet\sdk]
  3.1.402 [C:\Program Files\dotnet\sdk]
  5.0.100-rc.1.20452.10 [C:\Program Files\dotnet\sdk]
  5.0.100-rc.1.20454.5 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview9.19424.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.0-rc.1.20451.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.3-servicing-26724-03 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0-preview.7.20364.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0-rc.1.20451.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.0-rc.1.20452.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
ghost commented 4 years ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

user72356 commented 3 years ago

Same issue here when trying to send objects from JS to .Net in a callback situation. I would like to receive the objects as IJSObjectReference on the .Net side and not have JSON attempt to serialize them. Is there an inverse to attachReviver so that I may influence the serialization process from JS to .Net? Is it attachDispatcher??

Jasonizback commented 3 years ago

This my device I'm the owner here so whoever that user is please deny access to my private repository list

jag

On Tue, Sep 14, 2021, 1:24 PM user72356 @.***> wrote:

Same issue here when trying to send objects from JS to .Net in a callback situation. I would like to receive the objects as IJSObjectReference on the .Net side and not have JSON attempt to serialize them. Is there an inverse to attachReviver so that I may influence the serialization process from JS to .Net? Is it attachDispatcher??

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dotnet/aspnetcore/issues/26049#issuecomment-919405240, or unsubscribe https://github.com/notifications/unsubscribe-auth/AU3JA5SS3BSJENWHV4W6DCTUB6HO7ANCNFSM4RRWPYNA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

user72356 commented 3 years ago

You've been hacked! Evil laughter

TanayParikh commented 3 years ago
  • What is an "Unexcepted JSON property"? Did you mean "Unexpected"?

Yes, this was a typo and has been fixed for 6.0

KristofferStrube commented 2 years ago

The progress on solving this problem is also kept track of in issue #31151

ghost commented 2 years ago

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

SteveSandersonMS commented 1 year ago

This is a bug in the application code. The JavaScript code should look like this:

dotNetObj.invokeMethod(dotNetMethod, DotNet.createJSObjectReference(e));

That is, you must explicitly construct the JS object reference via DotNet.createJSObjectReference for calls originating from JS, otherwise the system has no way to know that's how you want to serialize the parameter value.

@guardrex Do you know if this is covered in docs anywhere? I searched through https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-7.0 and the createJSObjectReference API doesn't appear to be mentioned. This might be something we missed.

SteveSandersonMS commented 1 year ago

Yes, this was a typo and has been fixed for 6.0

@TanayParikh Was it definitely fixed? I was still able to repro this message on 7.0:

image

guardrex commented 1 year ago

@SteveSandersonMS ... Correct ... not covered ... so I'll open an issue and hopefully reach it by the end of next week. I have about a week of .NET 7 wrap-up things to do first.

TanayParikh commented 1 year ago

Was it definitely fixed? I was still able to repro this message on 7.0:

That was in reference to a correction I'd made in one of my streaming/byte[] interop PRs. But just took another look and there are a lot of places we say excepted instead of expected.

https://github.com/dotnet/aspnetcore/pull/44989

guardrex commented 1 year ago

@SteveSandersonMS @javiercn ... In @JinShil's example (in the context of creating a doc example to demonstrate this), why doesn't the event marshal back to .NET as a MouseEventArgs?

[JSInvokable]
public void OnClick(MouseEventArgs args)
{
    Console.WriteLine($"Mouse coordinates: {args.ClientX}:{args.ClientY}");
}

... because I think that the click event makes the Event (e) a MouseEvent. When I read e.clientX and e.clientY, I do get mouse coordinates. However on the .NET side, I'm getting ...

Mouse coordinates: 0:0

No 💥 ... but no values.

If it turns out that it should marshal back as a IJSObjectReference in this case (i.e., a click event on a <div>), what would be good to do with objRef to complete the demo for this?

[JSInvokable]
public void OnClick(IJSObjectReference objRef)
{
   // ??????????
}
SteveSandersonMS commented 1 year ago

In @JinShil's example (in the context of creating a doc example to demonstrate this), why doesn't the event marshal back to .NET as a MouseEventArgs?

Because it isn't one. It's a JavaScript MouseEvent object, which not the same type as a .NET MouseEventArgs (even though they are clearly related to the same concept). For real Blazor event handlers, Blazor contains internal code that explicitly marshals between the JS event types and the .NET event types, passing the specific subset of data that makes sense to serialize. But this logic isn't a real Blazor event handler; it's just a JS interop call, so Blazor will just do normal JSON serialization without any other special-case marshalling.

SteveSandersonMS commented 1 year ago

If it turns out that it should marshal back as a IJSObjectReference in this case (i.e., a click event on a

), what would be good to do with objRef to complete the demo for this?

I wouldn't have an example based on event handlers, because Blazor has a proper native way to deal with events, and it's not this.

Instead I'd simply show an example of sending some arbitrary object from JS to .NET as an IJSObjectReference. For example,

// Passes a reference to the non-serializable 'window' object to .NET code, which receives it as an IJSObjectReference
DotNet.invokeMethodAsync('MyApp', 'ReceiveWindowObject', DotNet.createJSObjectReference(window));
guardrex commented 1 year ago

Very well ... one final question about an API remark for createJSStreamReference, which I'm also placing in the doc ...

@param streamReference The ArrayBufferView or Blob used to create the JavaScript stream reference.

export function createJSStreamReference(streamReference: ArrayBuffer | ArrayBufferView | Blob | any)

I can't cross-link "ArrayBufferView" at MDN (or in the ECMAScript spec either). I see it described around the Net as a "superclass" for accessing the bytes of ArrayBuffer ... or an "abstract type" for various arrays, but MDN doesn't have a dedicated page to link for it. I was angling the text to read something like this ...

Call DotNet.createJSStreamReference(streamReference) to construct a JS stream reference so that it can be passed to .NET, where streamReference is an ArrayBuffer, ArrayBufferView, or Blob used to create the JS stream reference.

No example on that one. I was just going to place it under the new content for createJSObjectReference in a section titled Create JavaScript object and data references that can be passed to .NET.

Is an "ArrayBufferView" a DataView of an ArrayBuffer and/or a TypedArray of an ArrayBuffer?

Or ... should I just leave "ArrayBufferView" code-fenced without an MDN cross-link?

SteveSandersonMS commented 1 year ago

Is an "ArrayBufferView" a DataView of an ArrayBuffer and/or a TypedArray of an ArrayBuffer?

I think ArrayBufferView is just the abstract base class for all the different kinds of typed array. So I'd probably say something like:

Call DotNet.createJSStreamReference(streamReference) to construct a JS stream reference so that it can be passed to .NET, where streamReference is an ArrayBuffer, Blob, or any typed array such as Uint8Array or Float32Array used to create the JS stream reference.

TBH the parameter name streamReference is really strange. It should probably be called dataSource or something like that.

guardrex commented 1 year ago

Ok, I'm good. I'll wrap this up now. If you want me to ping u on the PR, let me know, but I don't think it's necessary based on how closely the text will come off of your remarks (and the example).

SteveSandersonMS commented 1 year ago

Thanks. Closing this issue now then.