Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

partial view bug of MVC website in .NET Core 2.1 RC1 #785

Closed
javiercn opened this issue May 10, 2018 · 16 comments
Closed

partial view bug of MVC website in .NET Core 2.1 RC1 #785

javiercn opened this issue May 10, 2018 · 16 comments

Comments

@javiercn
Copy link
Member

From @karelz on May 10, 2018 17:37

From @hez2010 on May 8, 2018 4:33

I use .NET Core 2.1.300-rc1 to create a new MVC web app with individual authentication, then I use identity scaffolder (visual studio -> right click my project -> add -> new scaffolded item -> identity) and specify layout file with "~/Views/Shared/_Layout.cshtml" to generate identity related code in Areas/Identity automatically.

When I try to build, the compiler warns me that I need to use <partial /> instead of @Html.Partial. Of course I can replace @Html.Partial to <partial /> manually, but why not generate code with <partial /> automatically at the beginning?

And then I run the website, when I navigated to Login/Register page in browser, I found that the _LoginPartial View could not be loaded correctly that it still remained <a asp-area="Identity" asp-page="/Account/Login"></a> but not <a href="/Identity/Account/Login"></a> in HTML source so that I could not click Login or Register again. Also, I changed the navbar style in _LoginPartial.cshtml but when I navigated to Login/Register page, it restored to original styles.

Normally (in home page):
1

In Login/Register page:
2

But after I changed the code in _Layout.cshtml:
<partial name="_LoginPartial" /> -----> <partial name="_LoginPartial.cshtml" />, it works again.

3

Hope that you can fix this bug, thanks!

Copied from original issue: dotnet/corefx#29566

Copied from original issue: aspnet/Mvc#7768

@javiercn
Copy link
Member Author

From @karelz on May 10, 2018 17:37

From @kasper3 on May 8, 2018 7:12

this belongs to https://github.com/aspnet/razor or https://github.com/aspnet/mvc

@javiercn
Copy link
Member Author

From @pranavkm on May 10, 2018 17:49

@javiercn does scaffolding need to be updated?

@javiercn
Copy link
Member Author

From @hez2010 on May 10, 2018 17:56

I found a new bug.
the scaffolder will ignore the layout file which user specified and keeps ~/Areas/Identity/Pages/Account/Manage/_Layout.cshtml 's layout file as "/Areas/Identity/Pages/_Layout.cshtml" despite the file /Areas/Identity/Pages/_Layout.cshtml doesn't exist.

@javiercn
Copy link
Member Author

@seancpeters Is this fixed in RTM with your changes?

@seancpeters
Copy link
Contributor

seancpeters commented May 10, 2018

@javiercn - I created a MVC individual auth project using:
"Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.0-rtm-30762"

There are multiple issues mentioned in this thread, I'll try to address them all:

The _Layout.cshtml includes <partial name="_LoginPartial" />

I can navigate corerctly between the Register & Login pages. The source of the login page and the register page includes:
image

I'm not clear what this is trying to point out:

the scaffolder will ignore the layout file which user specified and keeps ~/Areas/Identity/Pages/Account/Manage/_Layout.cshtml 's layout file as "/Areas/Identity/Pages/_Layout.cshtml" despite the file /Areas/Identity/Pages/_Layout.cshtml doesn't exist.

Can yiou help me understand this part?

@hez2010
Copy link

hez2010 commented May 11, 2018

@seancpeters I mean that after scaffolding, the layout file of /Areas/Identity/Pages/Account/Manage/_Layout.cshtml should be ~/Views/Shared/_Layout.cshtml, but actually it is /Areas/Identity/Pages/_Layout.cshtml.

you can see my commit: https://github.com/hez2010/hjudgeNEW/commit/9f7dbe482d202fa8683d44a36328d5e89c59ab2d#diff-7826a144e5b74bb0e26273eaf58194d4
In order to fix it manually, I changed Layout = "/Areas/Identity/Pages/_Layout.cshtml"; to Layout = "~/Views/Shared/_Layout.cshtml";

@seancpeters
Copy link
Contributor

This is correctly now correctly dealt with - the user specified layout file is respected.

@chrisckc
Copy link

chrisckc commented May 18, 2018

@seancpeters I also I used .NET Core 2.1.300-rc1 to create a new MVC web app with individual authentication, then I used identity scaffolder, but did not specify a layout file, just left it blank and then let it generate the identity related code in Areas/Identity.

The scaffolded Identity UI seems to be a bunch of Razor Pages , so when viewing the Identity UI, the Layout will be switched over to the layout specified in /Pages/Shared/_Layout.cshtml rather than /Views/Shared/_Layout.cshtml which is for the main part of the app which consists of MVC pages.

@javiercn I also noticed that inside the file:
~/Areas/Identity/Pages/Account/Manage/_Layout.cshtml
Layout = "/Areas/Identity/Pages/_Layout.cshtml";
That file does not exist, but the logout button still shows when viewing the account management pages. The issue is that the Logout button does not work, it posts to the current page (/Identity/Account/Manage) resulting in a 400 bad request.

Looking at the page source, it seems that the _LoginPartial.cshtml razor page has not been rendered properly as the 'asp-' tags are visible in the html source like this:
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnurl="/?page=%2FIndex" method="post" id="logoutForm" class="navbar-right">
It should look like this as it does from the Home/Index page:
<form method="post" id="logoutForm" class="navbar-right" action="/Identity/Account/Logout?returnUrl=%2F">

This means that out of the box, the scaffolded Identity UI does not work properly, the logout button only works from outside of the account management pages, ie. from the MVC views. Once inside the Razor Pages, the Logout button is broken.

I have tried various things:

I have confirmed that the "/Pages/Shared/_Layout.cshtml" file is actually being used despite what is specified in the area layout file as i changed the "Logout" button text inside "/Pages/Shared/_LoginPartial.cshtml" and could see the updated text when inside the account management pages.

After reading:
https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-2.1
I went into "/Pages/Shared/_Layout.cshtml" , I changed line 41 from:
await Html.RenderPartialAsync("_LoginPartial");
to
await Html.PartialAsync("_LoginPartial");
thinking that may be why the LoginPartial.cshtml is not being rendered properly, but it made no difference.

After reading the linked issue:
aspnet/Mvc#7768
I tried changing it to:
await Html.PartialAsync("_LoginPartial.cshtml");
and
await Html.RenderPartialAsync("_LoginPartial.cshtml");
and
<partial name="_LoginPartial" />
and
<partial name="_LoginPartial.cshtml" />
again no difference.

I am not sure if this issue should sit here or with Razor?

To try and get it to work i ditched the 'asp-' tags and changed the form inside _LoginPartial.cshtml to:
<form action="/Identity/Account/Logout?returnUrl=%2F" method="post" id="logoutForm" class="navbar-right">

The Logout button now correctly posts to /Identity/Account/Logout, but i still get a 400 bad request and the OnPost method in the Logout.cshtml.cs file is not being fired. Looking at the Chrome inspector, the form post looks identical to the one from the Home/Index page which works fine.

Looking at the network requests they are also identical, the one from Home/Index works fine and procuces a 302 redirect to '/' The one from /Identity/Account/Manage produces a 400 bad request and does not fire the OnPost method. I think this needs a fresh pair of eyes so ill come back to it another day, almost midnight in the UK.

@chrisckc
Copy link

chrisckc commented May 22, 2018

I have had chance to take another look at this and found out why i can logout successfully from the root fo the site but not from the account management UI (/Identity/Account/Manage).

The source of the root home page contains a RequestVerificationToken in the html body inside the "logoutForm" element:

<form method="post" id="logoutForm" class="navbar-right" action="/Identity/Account/Logout?returnUrl=%2F">
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a title="Manage" href="/Identity/Account/Manage">Hello bobtest@example.org!</a>
            </li>
            <li>
                <button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
            </li>
        </ul>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8DxKt2Y1qMpIpID........" />
</form>

When the form is Posted to /Identity/Account/Logout, the token is sent in the request body:

__RequestVerificationToken=CfDJ8DxKt2Y1qMpIpID........

However the /Identity/Account/Manage page contains the same logout form but without the RequestVerificationToken token:

<form action="/Identity/Account/Logout?returnUrl=%2F" method="post" id="logoutForm" class="navbar-right">
        <ul class="nav navbar-nav navbar-right">
            <li>
                <a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello bobtest@example.org!</a>
            </li>
            <li>
                <button type="submit" class="btn btn-link navbar-btn navbar-link">Logout me</button>
            </li>
        </ul>
    </form>

So the Post to /Identity/Account/Logout does not contain the token, resulting in a failed request.

This can be seen in the output log which i failed to notice at first:

info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.AutoValidateAntiforgeryTokenAuthorizationFilter[1]
Antiforgery token validation failed. The required antiforgery request token was not provided in either form field "__RequestVerificationToken" or header value "RequestVerificationToken".
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The required antiforgery request token was not provided in either form field "__RequestVerificationToken" or header value "RequestVerificationToken".

It looks like the token is automatically added to forms as there is no attribute or code in the scaffolded files or the MVC template file which specifically adds it.

As mentioned in my previous post, the original issue is that the /Pages/Shared/_LoginPartial.cshtml razor page is not being rendered correctly preventing the logout from working, so as a quick hack to get it to work until this is fixed i added the form action manually.

The missing RequestVerificationToken in the form html is probably because the _LoginPartial.cshtml is not being rendered correctly. Maybe this can be manually added but it's going to get a bit too hacky, i hope this will be fixed before RTM.

@hez2010
Copy link

hez2010 commented May 22, 2018

The _LoginPartial.cshtml has existed in /View/Shared/, but the scaffolder still created one in /Pages/Shared/
After deleting /Pages/Shared/_LoginPartial.cshtml, the problem I mentioned above was gone.

@chrisckc
Copy link

Deleting /Pages/Shared/_LoginPartial.cshtml has indeed solved the above problem, thanks!

However now when i click the 'Login' link i get this:

InvalidOperationException: The partial view '_LoginPartial.cshtml' was not found. The following locations were searched:
/Pages/Shared/_LoginPartial.cshtml

So i tried also removing /Pages/Shared/_Layout.cshtml but also get an exception with cant find the _Layout.cshtml file.

In both cases the Register and Login links are there, as they are coming from the Views/Shared folders when looking at the MVC root. However when clicking on the Login link it loads the Razor pages which will presumably use /Pages/Shared/ so results in the error. For some reason when i was already signed in after deleting the file and looking at the account management UI, the Logout link was there and worked, despite the Pages/Shared/_LoginPartial.cshtml being deleted.

@hez2010
Copy link

hez2010 commented May 22, 2018

You may also need to change the code in /Areas/Identity/Pages/_ViewStart.cshtml, replacing "/Pages/Shared/_Layout.cshtml" with "~/Views/Shared/_Layout.cshtml"
@chrisckc

@chrisckc
Copy link

chrisckc commented May 22, 2018

@hez2010 That did the trick thanks, didn't know that it was ok to share the MVC view layouts with the Razor Pages as the Identity scaffolder created separate /Pages/Shared/ layouts so i figured there was a valid reason that the scaffolder did that.

For what i want i prefer to share the base MVC layout, but it looks like if you did want to use a separate layout for the Razor pages it is not currently possible due to an issue rendering the 'Razor Pages' layout files.

The issue is now resolved for the evaluation i was performing, but there is a major issue with the Identity Scaffolder as it does not produce a working set of files. Everything works ok until you run the Identity Scaffolder, after that you are unable to logout from within the account management pages.

There were no options available in the scaffolder to get this right from the start, the only options are "Select and existing layout page, or specify a new one" but this seems to be referring to the layout which adds the left side navigation menu within the account management pages, not the master layout which includes the top nav menu and Login/Logout/Register links.

Also in the scaffolder, there is no way to select an existing or add a new custom user subclass, the selection box is greyed out, but i can select the data context class. This meant that i had to manually update all of the the scaffolded files with a search and replace to use the custom user subclass.

Btw. I am using Visual Studio 15.7.1 on Windows

@seancpeters
Copy link
Contributor

seancpeters commented May 22, 2018

@chrisckc - If you choose to create a new data context class, the User Class selector becomes active, and the new data context class will use the newly created user class.

When selecting an existing data context class, it's not possible to add a new user class - the existing data context class was already setup with either an existing custom user class, or the default IdentityUser.

@Skimt
Copy link

Skimt commented Sep 1, 2018

Also notice that ViewData["Title"] in /Pages/Shared/_Layout.cshtml doesn't update while browsing Identity pages. You can set a permanent title e.g. ViewData["Title"] = "Profile"; in /Pages/Account/Manage/_Layout.cshtml But before you can even begin to make changes in Identity pages you need to do what hez2010 said, by changing Layout to /Pages/Shared/_Layout.cshtml.

@Sm1le291
Copy link

Sm1le291 commented Jul 12, 2020

Still was not fixed. When I scaffold Identity pages in Blazor project face with the same problem.
Two years have gone already, Microsoft really?????

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants