|
2 | 2 |
|
3 | 3 | **Cross-frame messaging and event handling for BlazorFrame** |
4 | 4 |
|
5 | | -This guide covers all aspects of configuring communication between your Blazor application and iframe content, including message validation, origin control, and event handling. |
| 5 | +This guide covers all aspects of configuring communication between your Blazor application and iframe content, including message validation, origin control, event handling, and **bidirectional communication**. |
6 | 6 |
|
7 | 7 | ## Message Handling Overview |
8 | 8 |
|
9 | | -BlazorFrame provides two main approaches to handling messages: |
| 9 | +BlazorFrame provides comprehensive communication capabilities: |
10 | 10 |
|
11 | | -- **Validated Messages** (`OnValidatedMessage`) - Recommended for new implementations |
| 11 | +- **Iframe -> Host** (`OnValidatedMessage`) - Receive messages from iframe with validation |
| 12 | +- **Host -> Iframe** (`SendMessageAsync`) - Send messages to iframe with security validation |
12 | 13 | - **Raw Messages** (`OnMessage`) - Legacy support for simple scenarios |
13 | 14 |
|
14 | 15 | ## Basic Message Configuration |
15 | 16 |
|
16 | | -### Essential Message Handling |
| 17 | +### Bidirectional Communication |
17 | 18 |
|
18 | 19 | ```razor |
19 | | -<BlazorFrame Src="@widgetUrl" |
20 | | - SecurityOptions="@messageOptions" |
21 | | - OnValidatedMessage="HandleValidatedMessage" |
22 | | - OnSecurityViolation="HandleSecurityViolation" /> |
| 20 | +<BlazorFrame @ref="iframeRef" |
| 21 | + Src="@widgetUrl" |
| 22 | + OnValidatedMessage="HandleMessage" /> |
| 23 | +
|
| 24 | +<button class="btn btn-primary" @onclick="SendDataToIframe"> |
| 25 | + Send Data to Iframe |
| 26 | +</button> |
23 | 27 |
|
24 | 28 | @code { |
25 | | - private readonly MessageSecurityOptions messageOptions = new MessageSecurityOptions() |
26 | | - .ForProduction() |
27 | | - .WithBasicSandbox(); |
| 29 | + private BlazorFrame? iframeRef; |
28 | 30 | |
29 | | - private async Task HandleValidatedMessage(IframeMessage message) |
| 31 | + // Send structured data to iframe |
| 32 | + private async Task SendDataToIframe() |
30 | 33 | { |
31 | | - Logger.LogInformation("Received message from {Origin}: {Data}", |
32 | | - message.Origin, message.Data); |
33 | | - |
34 | | - // Process the validated message |
35 | | - await ProcessMessage(message); |
| 34 | + if (iframeRef == null) return; |
| 35 | + |
| 36 | + var success = await iframeRef.SendMessageAsync(new |
| 37 | + { |
| 38 | + type = "data-update", |
| 39 | + timestamp = DateTime.UtcNow, |
| 40 | + data = new |
| 41 | + { |
| 42 | + userId = currentUser.Id, |
| 43 | + preferences = currentUser.Preferences, |
| 44 | + theme = currentTheme |
| 45 | + } |
| 46 | + }); |
| 47 | + |
| 48 | + if (success) |
| 49 | + { |
| 50 | + Logger.LogInformation("Data sent successfully to iframe"); |
| 51 | + } |
36 | 52 | } |
37 | 53 | |
38 | | - private async Task HandleSecurityViolation(IframeMessage violation) |
| 54 | + // Send typed messages with automatic structure |
| 55 | + private async Task SendNotification() |
39 | 56 | { |
40 | | - Logger.LogWarning("Security violation: {Error}", violation.ValidationError); |
41 | | - |
42 | | - // Handle security issues |
43 | | - await HandleSecurityIssue(violation); |
| 57 | + await iframeRef.SendTypedMessageAsync("notification", new |
| 58 | + { |
| 59 | + message = "Hello from host!", |
| 60 | + level = "info", |
| 61 | + timestamp = DateTimeOffset.UtcNow |
| 62 | + }); |
| 63 | + } |
| 64 | + |
| 65 | + private async Task HandleMessage(IframeMessage message) |
| 66 | + { |
| 67 | + if (message.MessageType == "request-user-data") |
| 68 | + { |
| 69 | + // Respond to iframe's request for user data |
| 70 | + await SendUserDataToIframe(); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + private async Task SendUserDataToIframe() |
| 75 | + { |
| 76 | + await iframeRef.SendTypedMessageAsync("user-data-response", new |
| 77 | + { |
| 78 | + user = new |
| 79 | + { |
| 80 | + id = currentUser.Id, |
| 81 | + name = currentUser.Name, |
| 82 | + email = currentUser.Email, |
| 83 | + permissions = currentUser.Permissions |
| 84 | + } |
| 85 | + }); |
44 | 86 | } |
45 | 87 | } |
46 | 88 | ``` |
@@ -336,139 +378,6 @@ public class MessageProcessor |
336 | 378 | } |
337 | 379 | ``` |
338 | 380 |
|
339 | | -## Bidirectional Communication |
340 | | - |
341 | | -### Sending Messages to Iframe |
342 | | - |
343 | | -```razor |
344 | | -<BlazorFrame @ref="iframeRef" |
345 | | - Src="@widgetUrl" |
346 | | - OnValidatedMessage="HandleMessage" /> |
347 | | -
|
348 | | -<button class="btn btn-primary" @onclick="SendDataToIframe"> |
349 | | - Send Data to Iframe |
350 | | -</button> |
351 | | -
|
352 | | -@code { |
353 | | - private BlazorFrame? iframeRef; |
354 | | - |
355 | | - private async Task SendDataToIframe() |
356 | | - { |
357 | | - if (iframeRef == null) return; |
358 | | - |
359 | | - var messageData = new |
360 | | - { |
361 | | - type = "data-update", |
362 | | - timestamp = DateTime.UtcNow, |
363 | | - data = new |
364 | | - { |
365 | | - userId = currentUser.Id, |
366 | | - preferences = currentUser.Preferences, |
367 | | - theme = currentTheme |
368 | | - } |
369 | | - }; |
370 | | - |
371 | | - await iframeRef.SendMessageAsync(messageData); |
372 | | - } |
373 | | - |
374 | | - private async Task HandleMessage(IframeMessage message) |
375 | | - { |
376 | | - if (message.MessageType == "request-user-data") |
377 | | - { |
378 | | - // Respond to iframe's request for user data |
379 | | - await SendUserDataToIframe(); |
380 | | - } |
381 | | - } |
382 | | - |
383 | | - private async Task SendUserDataToIframe() |
384 | | - { |
385 | | - var userData = new |
386 | | - { |
387 | | - type = "user-data-response", |
388 | | - user = new |
389 | | - { |
390 | | - id = currentUser.Id, |
391 | | - name = currentUser.Name, |
392 | | - email = currentUser.Email, |
393 | | - permissions = currentUser.Permissions |
394 | | - } |
395 | | - }; |
396 | | - |
397 | | - await iframeRef.SendMessageAsync(userData); |
398 | | - } |
399 | | -} |
400 | | -``` |
401 | | - |
402 | | -### Request-Response Pattern |
403 | | - |
404 | | -```razor |
405 | | -@code { |
406 | | - private readonly Dictionary<string, TaskCompletionSource<object>> pendingRequests = new(); |
407 | | - |
408 | | - private async Task<T> SendRequestToIframe<T>(string requestType, object data) |
409 | | - { |
410 | | - var requestId = Guid.NewGuid().ToString(); |
411 | | - var tcs = new TaskCompletionSource<object>(); |
412 | | - |
413 | | - pendingRequests[requestId] = tcs; |
414 | | - |
415 | | - try |
416 | | - { |
417 | | - // Send request to iframe |
418 | | - await iframeRef.SendMessageAsync(new |
419 | | - { |
420 | | - type = requestType, |
421 | | - requestId = requestId, |
422 | | - data = data |
423 | | - }); |
424 | | - |
425 | | - // Wait for response with timeout |
426 | | - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); |
427 | | - cts.Token.Register(() => tcs.TrySetCanceled()); |
428 | | - |
429 | | - var response = await tcs.Task; |
430 | | - return JsonSerializer.Deserialize<T>(response.ToString()); |
431 | | - } |
432 | | - finally |
433 | | - { |
434 | | - pendingRequests.Remove(requestId); |
435 | | - } |
436 | | - } |
437 | | - |
438 | | - private async Task HandleMessage(IframeMessage message) |
439 | | - { |
440 | | - // Handle responses to our requests |
441 | | - if (message.MessageType?.EndsWith("-response") == true) |
442 | | - { |
443 | | - var responseData = JsonSerializer.Deserialize<ResponseMessage>(message.Data); |
444 | | - |
445 | | - if (pendingRequests.TryGetValue(responseData.RequestId, out var tcs)) |
446 | | - { |
447 | | - tcs.SetResult(responseData.Data); |
448 | | - } |
449 | | - } |
450 | | - } |
451 | | - |
452 | | - // Usage example |
453 | | - private async Task GetIframeData() |
454 | | - { |
455 | | - try |
456 | | - { |
457 | | - var iframeData = await SendRequestToIframe<IframeDataResponse>( |
458 | | - "get-data", |
459 | | - new { category = "user-preferences" } |
460 | | - ); |
461 | | - |
462 | | - Logger.LogInformation("Received iframe data: {Data}", iframeData); |
463 | | - } |
464 | | - catch (OperationCanceledException) |
465 | | - { |
466 | | - Logger.LogWarning("Request to iframe timed out"); |
467 | | - } |
468 | | - } |
469 | | -} |
470 | | -``` |
471 | | - |
472 | 381 | ## Event Handling |
473 | 382 |
|
474 | 383 | ### Comprehensive Event Configuration |
|
0 commit comments