Reputation: 133
I have a Blazor page that can receive form-data from a POST request. After this data is received, it will be set to two properties and also rendered onto the page. The problem is that after this has happened, the page will re-rendered and the data of the properties will be discarded. Does somebody know why this is happening and what the best way is to keep my POST data, or stop the page from re-rendering?
I am currently using Blazor Server .net 6. I have already tried changing the render-mode from "ServerPrerendered" to "Server", but that doesn't solve the problem. Changing the render-mode to "Static" does prevent Blazor from re-rendering the page, but I don’t want to have static pages.
Here is my _Host.cshtml:
@page "/"
@namespace PostToRazorPageTest.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = "_Layout";
}
<component type="typeof(App)" render-mode="ServerPrerendered" />
@attribute [IgnoreAntiforgeryToken]
@model HostPageModel
Here you can see the HostPageModel class, which I use to catch the form-data. It uses a PostFormService, which is a scoped service with one property that contains de Form data. Changing it to a Singleton does prevent the POST data from being discarded, but also forms a big privacy risk.
using Microsoft.AspNetCore.Mvc.RazorPages;
using PostToRazorPageTest.Services;
namespace PostToRazorPageTest.Pages
{
public class HostPageModel : PageModel
{
// postFormService is injected by the DI
public HostPageModel(PostFormService postFormService)
{
PostFormService = postFormService;
}
private PostFormService PostFormService { get; }
public void OnPost()
{
// store the post form in the PostFormService
PostFormService.Form = Request.Form;
}
}
}
The razor page where I read the form-data:
@page "/showdata"
@inject PostFormService PostFormService;
<PageTitle>Show POST data</PageTitle>
<h1>Show POST data</h1>
@if (Field1 != null && Field2 != null)
{
<p>Field 1 value: @Field1</p>
<p>Field 2 value: @Field2</p>
}
else
{
<p>
<b>
The request wasn't made with a POST request, or the request was missing data for the field called "field1" and/or "field2".
</b>
</p>
}
@code {
private string? Field1 { get; set; }
private string? Field2 { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
if (PostFormService.Form != null)
{
Field1 = PostFormService.Form["field1"];
Field2 = PostFormService.Form["field2"];
}
}
}
Upvotes: 0
Views: 3459
Reputation: 30036
This is a comment rather than a complete answer: I can't put example code into a comment!
You can do something like this with your service to make the request unique, and then pass the Guid - probably as a string into App
.
public class DataService
{
private Dictionary<Guid, FormData> _formData = new Dictionary<Guid, FormData>();
public Guid AddFormData(FormData data)
{
var id = Guid.NewGuid();
_formData.Add(id, data);
this.ClearData();
return id;
}
public bool TryGetFormData(Guid guid, out FormData data)
{
data = _formData[guid];
this.ClearData();
return data != null;
}
// clears old data
private void ClearData()
{
var list = _formData.Where(item => item.Value.TimeStamp.AddMinutes(5) <= DateTime.Now).ToList();
list.ForEach(item => _formData.Remove(item.Key));
}
}
// example form data class
public class FormData
{
public string Name { get; set; } = string.Empty;
public DateTime TimeStamp { get; } = DateTime.Now;
}
You can pass parameters into App.razor
in _Host,html like this:
@(await Html.RenderComponentAsync<BlazorApp2.App>( RenderMode.ServerPrerendered,new {FormGuid="1234"}))
@*<component type="typeof(App)" render-mode="ServerPrerendered" />*@
You can then have a scoped service to hold the form data:
// Set up as Scoped i.e. one per user session
public class MyFormDataService
{
private readonly DataService dataService;
public FormData? FormData { get; private set }
public MyFormDataService(DataService dataService)
=> this.dataService = dataService;
public void GetFormData(Guid id)
{
if (dataService.TryGetFormData(id, out FormData data))
this.FormData = data;
}
}
And load the service in App
@inject MyFormDataService dataService
<div>@this.FormGuid</div>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@code{
[Parameter] public Guid FormGuid { get; set; }
protected override void OnInitialized()
=> this.dataService.GetFormData(FormGuid);
}
And consume it wherever by injecting the DI instance of MyFormDataService
.
Upvotes: 1