AQuirky
AQuirky

Reputation: 5236

What is difference between Page() and Rediirect() to self in ASP.NET Core Razor pages?

I have a very simple page that has 2 forms. When I submit one form it resets the other. There is some kind of hidden optimization is going on because when I refresh the page it presents the correct result.

Here is the page:

<div asp-validation-summary="All"></div>
<div class="col-md-3">
   <form method="POST">
        <fieldset>
            <div>Host Name: <input asp-for="ClientConfig.HostName" /></div>
            <div>Responses in HTML? <input type="checkbox" asp-for="ClientConfig.Html" /></div>
            <input type="submit" asp-page-handler="ClientConfiguration" />
        </fieldset>
   </form>
   <p>Base URL = @Model.ClientConfig.Summary</p>
</div>
<form method="POST">
    <fieldset>
        <div>Name: <input asp-for="Customer.Name" /></div>
        <div>New? <input type="checkbox" asp-for="Customer.New" /></div>
        <input type="submit" asp-page-handler="Customer" />
    </fieldset>
</form>
<ul>
    <li>Customer = @Model.Customer.Summary</li>
</ul>

Here is the model...

public class ClientConfig
{
    public static ClientConfig Instance { get; set; } = new ClientConfig();
    [Required, StringLength(100)] public string HostName { get; set; } = "LocalHost";
    public bool Html { get; set; }
    public string Summary => HostName + (Html ? " (Html)" : "");
}
public class Customer
{
    public static Customer Instance { get; set; } = new Customer();
    [Required, StringLength(100)] public string Name { get; set; } = "Default";
    public bool New { get; set; }
    public string Summary => Name + (New ? " (New)" : "");
}
public class IndexModel : PageModel
{
    public IndexModel()
    {
        ClientConfig = ClientConfig.Instance;
        Customer = Customer.Instance;

    }
    [BindProperty] public ClientConfig ClientConfig { get; set; }
    [BindProperty] public Customer Customer { get; set; }
    public async Task<IActionResult> OnPostCustomerAsync()
    {
        Customer.Instance = Customer;
        return Page();
    }
    public async Task<IActionResult> OnPostClientConfigurationAsync()
    {
       ClientConfig.Instance = ClientConfig;
        return Page();
    }
}

So what is "return Page();" doing? According to the documentation it is simply rendering the current page. Not true. To verify this, simply refresh the page. It will be different, accurate with both forms filled in. Also if you replace "return Page()" with "return Redirect("/Index");" the result will also be accurate. So again, what is "return Page()" doing? There is some kind of undocumented optimization that resets all the forms except the one recently submitted.

Upvotes: 2

Views: 152

Answers (3)

AQuirky
AQuirky

Reputation: 5236

Right. After quite a long time of pondering this problem, I've finally figured it out. The problem: Razor pages moves in mysterious ways its wonders to perform.

My initial assumption was wrong. The page model constructor is not being bypassed. The page model is being properly constructed from static values. However after construction all bound objects on the page are reset. So this is not an "undocumented optimization"...it is an undocumented impairment.

The fix for this is to reset the page model from static values before returning Page().

    public async Task<IActionResult> OnPostCustomerAsync()
    {
        Customer.Instance = Customer;
        ClientConfig = ClientConfig.Instance;
        return Page();
    }
    public async Task<IActionResult> OnPostClientConfigurationAsync()
    {
       ClientConfig.Instance = ClientConfig;
       Customer = Customer.Instance;
        return Page();
    }

This is obviously a massive kluge, but no elegant solution exists. Anyone?

Upvotes: 0

poke
poke

Reputation: 387765

You have multiple separate forms on your page with separate form values: In one form you are submitting the client configuration object, in the other you are submitting the customer object.

So when you are actually submitting a form, only that form's data is being submitted. For example, if you are submitting the customer form, the client configuration data is not being transferred in the POST request (and the other way around).

As such, when you render the page by returning Page(), only the data that is currently in the page model is being rendered. If you are submitting the customer form, then only the customer data is available (same for the client configuration form).

This happens simply because you only have partial data on a page where you would need more to fill in all forms. If you want to prevent that, you will have to combine the data into a single model and form.

Now, if you refresh the page in the browser, then your browser is typically smart enough not to clear form values immediately. If you do a hard refresh using Ctrl + F5, then the browser should also reset the values.

It's also possible that your browser is performing an auto-fill for the forms here. This will typically only apply for GET requests. So that could be the reason why you are getting this result when you return a Redirect() because that completes the form POST with a GET request.

Upvotes: 1

Shaun Luttin
Shaun Luttin

Reputation: 141542

When I submit one form it resets the other.

That's the expected behavior for the way you coded your page. When the form POSTs to the server, the server does three things:

  1. creates a new IndexModel object using its constructor,
  2. binds the object's properties to the POSTed form values, and
  3. binds the object to its view.

In your code, step (1) resets properties to their default values. Step (2) overwrites those defaults with POSTed form values. Since you're submitting only one form, the other form's values retain their defaults. That's why submitting one resets the other.

So what is "return Page();" doing? According to the documentation it is simply rendering the current page. Not true. To verify this, simply refresh the page. It will be different, accurate with both forms filled in. Also if you replace "return Page()" with "return Redirect("/Index");" the result will also be accurate.

When you submit a form, return Page() renders the page in the context of a POST. On the other hand, when you refresh or redirect, the context is a GET. The difference you see happens because the context is different: the response to a POST is different from the response to a GET.

Upvotes: 0

Related Questions