Skip to content

Commit b79f4cf

Browse files
Give clearer errors if misusing this feature
1 parent 12aecc7 commit b79f4cf

File tree

1 file changed

+28
-0
lines changed

1 file changed

+28
-0
lines changed

src/Components/Endpoints/src/Rendering/SSRRenderModeBoundary.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public Task SetParametersAsync(ParameterView parameters)
4444
// call stack because the underlying buffer may get reused. This is enforced through a runtime check.
4545
_latestParameters = parameters.ToDictionary();
4646

47+
ValidateParameters(_latestParameters);
48+
4749
if (_prerender)
4850
{
4951
_renderHandle.Render(Prerender);
@@ -52,6 +54,32 @@ public Task SetParametersAsync(ParameterView parameters)
5254
return Task.CompletedTask;
5355
}
5456

57+
private void ValidateParameters(IReadOnlyDictionary<string, object?> latestParameters)
58+
{
59+
foreach (var (name, value) in latestParameters)
60+
{
61+
// There are many other things we can't serialize too, but give special errors for Delegate because
62+
// it may be a common mistake to try passing ChildContent when crossing rendermode boundaries.
63+
if (value is Delegate)
64+
{
65+
var valueType = value.GetType();
66+
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(RenderFragment<>))
67+
{
68+
throw new InvalidOperationException($"Cannot pass RenderFragment<T> parameter '{name}' to component '{_componentType.Name}' with rendermode '{_renderMode.GetType().Name}'. Templated content can't be passed across a rendermode boundary, because it is arbitrary code and cannot be serialized.");
69+
}
70+
else
71+
{
72+
// TODO: Ideally we *should* support RenderFragment (the non-generic version) by prerendering it
73+
// However it's very nontrivial since it means we have to execute it within the current renderer
74+
// somehow without actually emitting its result directly, wait for quiescence, and then prerender
75+
// the output into a separate buffer so we can serialize it in a special way.
76+
// A prototype implementation is at https://github.com/dotnet/aspnetcore/commit/ed330ff5b143974d9060828a760ad486b1d386ac
77+
throw new InvalidOperationException($"Cannot pass the parameter '{name}' to component '{_componentType.Name}' with rendermode '{_renderMode.GetType().Name}'. This is because the parameter is of the delegate type '{value.GetType()}', which is arbitrary code and cannot be serialized.");
78+
}
79+
}
80+
}
81+
}
82+
5583
private void Prerender(RenderTreeBuilder builder)
5684
{
5785
builder.OpenComponent(0, _componentType);

0 commit comments

Comments
 (0)