Jsinvokable Only On Public Method
ID |
csharp.jsinvokable_only_on_public_method |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
attributes, interop, reliability |
Description
Reports a method carrying the [JSInvokable] attribute that is not public. Both instance and
static methods are covered, and the accessibility is the effective one: a class or struct member
written with no access modifier at all is private and is reported, while an interface member
without a modifier is public and is not.
protected, internal, protected internal and private protected are all narrower than
public and are reported. The attribute is recognised in its short form, in the explicit
JSInvokableAttribute form and qualified with Microsoft.JSInterop.
Rationale
The JavaScript interop dispatcher resolves the target of DotNet.invokeMethodAsync by reflecting
over the public members of the type. A method marked [JSInvokable] with any narrower
accessibility can never be reached from JavaScript, so the attribute is dead code and every call
from the browser fails at run time with a "method not found" error.
Nothing warns about it beforehand. The attribute is valid on a method of any accessibility, so the compiler is satisfied and the declaration reads exactly like a working one. The defect surfaces only when the interop call is exercised, and the error arrives on the JavaScript side, far from the C# declaration that caused it.
public class Counter
{
[JSInvokable]
public void Increment() { } // OK
[JSInvokable]
public static void Reset() { } // OK, static is equally invokable
[JSInvokable]
private void Reload() { } // FLAW, unreachable from JavaScript
[JSInvokable]
void Restore() { } // FLAW, a class member is private by default
public void NotExposed() { } // OK, no attribute
}
Remediation
Make the annotated method public. The attribute is the statement that the method is part of the
component’s JavaScript-facing surface, so its accessibility has to say the same thing:
[JSInvokable]
public void Reload() { }
If the method is not meant to be called from JavaScript, remove the attribute instead — keeping it on a non-public method leaves a promise in the source that the runtime cannot keep.