简体   繁体   中英

ASP.NET Core MVC Identity - how to get current logged in user in view?

This is my UserIndex.cshtml view for a page which lists all posts that belong to the currently logged in user.

@model IEnumerable<AProject.Areas.Identity.Data.Post>
@using Microsoft.AspNetCore.Identity
@using AProject.Areas.Identity.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@{
    ViewData["Title"] = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h3> My posts </h3>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.DateCreated)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Category)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
            @foreach (var item in Model)
            {
                @if( @UserManager.GetUserName(User) == item.User.UserName )
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.DateCreated)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Category.Name)
                        </td>
                        <td>
                        <td>
                            <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                            <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                            <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
                        </td>
                    </tr>
                }
            }
  </tbody>
</table>

Apparently there is an error in the line with the @if( @UserManager.GetUserName(User) == item.User.UserName ) : NullReferenceException: Object reference not set to an instance of an object. I have no clue why I have this error because everything worked for a while (it listed in the table only posts by the logged in user ) then I was working on another view and when I got back to this page there was an error. I even tried with this @if( @User.Identity.Name == item.User.UserName ) but there is still an error. I even undid all the changes I made before the error and it still doesn't work. Also here is my controller if it helps:

[Authorize]
    public class PostsController : Controller
    {
        private readonly AProjectContext _context;

        private readonly UserManager<ApplicationUser> _userManager;
        public PostsController(AProjectContext context, UserManager<ApplicationUser> userManager)
        {
            _context = context;
            _userManager = userManager;
        }

        // GET: Posts
        public async Task<IActionResult> Index()
        {
            var aprojectContext = _context.Posts.Include(p => p.Category).Include(p => p.User);
            return View(await aprojectContext.ToListAsync());
        }

        public async Task<IActionResult> UserIndex()
        {
            
            var aprojectContext = _context.Posts.Include(p => p.Category).Include(p => p.User);
            return View(await aprojectContext.ToListAsync());
        }

        // GET: Posts/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var post = await _context.Posts
                .Include(p => p.Category)
                .Include(p => p.User)
                .FirstOrDefaultAsync(m => m.Id == id);
            if (post == null)
            {
                return NotFound();
            }

         
            return View(post);
        }

        // GET: Posts/Create
        public IActionResult Create()
        {
            ViewData["CategoryId"] = new SelectList(_context.Set<Category>(), "Id", "Name");
            return View();
        }

        // POST: Posts/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,Title,Summary,Content,CategoryId")] Post post)
        {
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); 
            var userName = User.FindFirstValue(ClaimTypes.Name); 
            ApplicationUser appUser = await _userManager.GetUserAsync(User);
            string userEmail = appUser?.Email; 

            post.DateCreated = DateTime.Now;
            post.User = appUser;
            post.UserId = userId;

            if (ModelState.IsValid)
            {
                _context.Add(post);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            ViewData["CategoryId"] = new SelectList(_context.Set<Category>(), "Id", "Name", post.Category.Name);
            return View(post);
        }

        // GET: Posts/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var post = await _context.Posts
                .Include(p => p.Category)
                .Include(p => p.User)
                .FirstOrDefaultAsync(m => m.Id == id);

            if (post == null)
            {
                return NotFound();
            }
            ViewData["CategoryId"] = new SelectList(_context.Set<Category>(), "Id", "Name", post.Category.Name);
            return View(post);
        }

        // POST: Posts/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("Id,Title,Summary,Content,CategoryId")] Post post)
        {
            if (id != post.Id)
            {
                return NotFound();
            }


            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(post);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!PostExists(post.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            ViewData["CategoryId"] = new SelectList(_context.Set<Category>(), "Id", "Name", post.Category.Name);
            return View(post);
        }

        // GET: Posts/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var post = await _context.Posts
                .Include(p => p.Category)
                .Include(p => p.User)
                .FirstOrDefaultAsync(m => m.Id == id);
            if (post == null)
            {
                return NotFound();
            }

            return View(post);
        }

        // POST: Posts/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var post = await _context.Posts.FindAsync(id);
            _context.Posts.Remove(post);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool PostExists(int id)
        {
            return _context.Posts.Any(e => e.Id == id);
        }
    

Is there a way to filter the posts by the currently logged in user in the controller then sending those posts to the view, instead filtering all posts in the view, if this still doesn't work?

**** UPDATE ****

It turns out the problem wasn't @UserManager.GetUserName(User) but the item.User.UserName because Post item didn't have a User. It works now. Also, if anyone has similar problems, I found it is better to filter posts in the controller which now looks like this

public async Task<IActionResult> UserIndex()
        {
            var userName = User.FindFirstValue(ClaimTypes.Name); // will give the user's userName
            var aprojectContext = _context.Posts.Include(p => p.Category).Include(p => p.User).Where(p => p.User.UserName == userName);
            return View(await aprojectContext.ToListAsync());
        }

Thanks everyone for suggestions and help.

Best you can do is save the UserId as a field for the posts. Then you get this UserId from the token/claims like mentioned by Ali Kianoor from the Identity server and you can filter the posts based on the UserId.

O and be sure to do it in the business logic/controller and not in your view. You need a clear separation of concern between the Model the View and the Controller in MVC.

Read more about SOC here

That happens if your session timed out and you need to re-log in.

A better way to refactor would be to do the user filter on your backend controller. Grab the session user in your controller and add a Where statement into your aprojectContext creation. How to get current user in asp.net core

Try this one for getting userId:

string userId = User.Claims.First(c => c.Type == "UserID").Value;

And for the getting user post, You should add a column to your table/view which is handling the userId for each post then you can filter your table/view base on your need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM