Couldn't avoid NullReferenceException in Action Filter (ASP.NET Core) - asp.net-core

I'm writing an action filter for setting LastAccessDate user property. On retrieving user's record from DB, i'm getting NullReferenceException. How to get rid of this exception? Here is my Action Filter:
public class LogActivity : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
var id = int.Parse(resultContext.RouteData.Values["id"].ToString());
var repo = resultContext.HttpContext.RequestServices.GetService<UserRepo>();
Console.WriteLine(id);
// var user = await repo.GetRespondent(id);
var user= repo.GetRespondent(id).Result; <========= Here Exception occurs
if (user != null)
{
user.LastAccessDate = DateTime.Now;
await repo.SaveAll();
}
}
}
Here is my UserRepo repository's get method:
public async Task<User> GetRespondent(int id)
{
var user= await _context.User.FirstOrDefaultAsync(u => u.Id == id);
if (user!= null)
return user
return null;
}

Replace this line
var user= repo.GetRespondent(id).Result; <========= Here Exception occurs
with
var user = await repo.GetRespondent(id);

Related

When I sign in with SignInAsync in ASP.NET Core Identity, RedirectToLocal is not authenticated

When I sign in with SignInAsync in ASP.NET Core Identity, RedirectToLocal is not authenticated.
If I log in without returning Url or go to allow anonymous action, it works fine, but when I redirect authenticate action return to the login page like the user never signs in. Still, I go to allow anonymous action and see the user sign in everything is okay.
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWithSms(string userId, string code, string returnUrl)
{
if (userId == null)
{
throw new ArgumentNullException(nameof(userId));
}
if (code == null)
{
throw new ArgumentNullException(nameof(code));
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
throw new ApplicationException(string.Format(ErrorConstant.UnableToLoadUser, userId));
}
var result = await _userManager.ConfirmEmailAsync(user, code);
if (!result.Succeeded)
{
return View("AccountError", this.GetErrorVm(AccountConstant.Persian.ConfirmSms, ErrorConstant.Persian.CodeWrong));
}
if (!user.PhoneNumberConfirmed)
{
user.PhoneNumberConfirmed = true;
_context.Users.Update(user);
_context.SaveChanges();
}
await _signInManager.SignInAsync(user, true);
await _setUserActivityLog.SetLogAsync(user.Id, AccountConstant.Persian.LoginToProfile);
return RedirectToLocal(string.IsNullOrEmpty(returnUrl) ? AccountConstant.Routes.ReturnUrlManageIndex : returnUrl);
}
redirect action:
[HttpGet]
[ActionDetail(menuCode: MenuConstant.ManageService.Code, name: "پاسخ دادن به تیکت")]
public async Task<IActionResult> TicketAnswer(long id)
{
var baseTicket = await _context.Tickets.Include(t => t.TicketType).Include(t => t.TicketRecords)
.ThenInclude(tr => tr.Person)
.SingleOrDefaultAsync(t => t.Id == id);
if (baseTicket == null)
{
return NotFound();
}
var vm = new ManageVm<TicketAnwserVm>
{
Entity = new TicketAnwserVm
{
QuickResponses = _context.QuickResponses.OrderBy(qr => qr.Title).Select(qr => new QuickResponseVm
{
Id = qr.Id,
Title = qr.Title
}),
Ticket = new TicketDisplayVm(baseTicket.StartDate)
{
Id = baseTicket.Id,
PersonId = baseTicket.PersonId,
State = baseTicket.State,
Subject = baseTicket.Subject,
TicketTypeName = baseTicket.TicketType.Name,
TicketRecords = baseTicket.TicketRecords.Join(_context.Users, tr => tr.PersonId,
u => u.PersonId,
(tr, u) => new TicketRecordVm(tr.Date)
{
Id = tr.Id,
PersonName = tr.Person.Name,
UserId = u.Id,
Content = tr.Content,
IsOwner = tr.IsOwner,
TicketId = tr.TicketId,
Status = tr.IsOwner ? TicketStatus.Out : TicketStatus.In
})
}
}
};
return View(vm);
}

Getting the result of Iactionresult in .net Core using nswagger studio

I have this api as you can see :
[HttpGet("CreateToken")]
public IActionResult CreateToken()
{
string tokenString = string.Empty;
tokenString = BuildJWTToken();
return Ok(new { Token = tokenString });
}
I use nswagger studio to generate my api code as you can see in my MVC core
public System.Threading.Tasks.Task CreateTokenAsync()
{
return CreateTokenAsync(System.Threading.CancellationToken.None);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public async System.Threading.Tasks.Task CreateTokenAsync(System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Default1/CreateToken");
var client_ = new System.Net.Http.HttpClient();
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = ((int)response_.StatusCode).ToString();
if (status_ == "200")
{
return;
}
else
if (status_ != "200" && status_ != "204")
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + (int)response_.StatusCode + ").", (int)response_.StatusCode, responseData_, headers_, null);
}
}
finally
{
if (response_ != null)
response_.Dispose();
}
}
}
finally
{
if (client_ != null)
client_.Dispose();
}
}
This code is generated by NswaggerStudio .
So when I want to call my createtoken API as you can see i couldn't get the token in the result:
public async Task<IActionResult> Index()
{
var q = myapi.CreateTokenAsync();
ViewBag.data = q;
return View(q);
}
And the view
<div class="text-center">
<h1 class="display-4">#ViewBag.data</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
And here you can see the result
Unfortunately, you cannot return result in this way.
To return any model your returning type in method should look like something like this
[HttpGet("CreateToken")]
public IActionResult<TokenModel> CreateToken()
{
string tokenString = string.Empty;
tokenString = BuildJWTToken();
return Ok(new { Token = tokenString });
}
Check this for more information
There is an alternative solution to this with ProducesResponseType (part of Microsoft.AspNetCore.Mvc).
Setting the typeof to the correct value you wish to return, will let swagger generate the json correctly. Which in turn allows nswag studio to generate
[HttpGet("CreateToken")]
[ProducesResponseType(typeof(Token), StatusCodes.Status200OK)]
public IActionResult CreateToken()
{
string tokenString = string.Empty;
tokenString = BuildJWTToken();
return Ok(new { Token = tokenString });
}

how to delete users including roles and users from AspnetRoleUsers

I am making a project using asp.net core 3.1 and I can't find the right source of deleting users including roles in Asp.net core 3.1 using Web.api
This is the code I have tried but seems like not appropriate but haven't tried yet. Do you have any ideas of how to realize that?
I want to appropriately check the error using Web Api functions such as statuscode or any error messages to the frontend.
[HttpPost, ActionName("Delete")]
public async Task<ActionResult> DeleteUser(string id)
{
var user = await _userManager.FindByIdAsync(id);
var rolesForUser = await _userManager.GetRolesAsync(user);
if (rolesForUser.Count() > 0)
{
foreach (var item in rolesForUser.ToList())
{
// item should be the name of the role
var result = await _userManager.RemoveFromRoleAsync(user, item);
}
}
await _userManager.DeleteAsync(user);
return OkResult(result);
}
You don't need to loop the roles and delete , you can use RemoveFromRolesAsync :
public virtual Task<IdentityResult> RemoveFromRolesAsync(TUser user, IEnumerable<string> roles);
And each operation will return IdentityResult which could be used to check the operation status :
if (User.Identity.IsAuthenticated)
{
try
{
var user = await _userManager.FindByIdAsync(id);
var roles= await _userManager.GetRolesAsync(user);
var result = await _userManager.RemoveFromRolesAsync(user, roles);
if (result.Succeeded)
{
var resultdelete = await _userManager.DeleteAsync(user);
if (result.Succeeded)
{
return Ok();
}
else
{
List<string> errors = new List<string>();
foreach (var error in result.Errors)
{
errors.Add(error.Description);
}
return BadRequest(errors);
}
}
else
{
List<string> errors = new List<string>();
foreach (var error in result.Errors)
{
errors.Add(error.Description);
}
return BadRequest(errors);
}
}
catch (Exception exception)
{
List<string> errors = new List<string>() { exception.Message };
return StatusCode(StatusCodes.Status500InternalServerError, errors);
}
}
But use database stored procedures with transaction is always a good choice .

How to update AppDBContext in ASP.NET Core Web API

I am quite new to ASP.NET and I am bit stuck with this.
I am creating an entry in my DB while registering the user:
private async Task<bool> CreateEntryInUserActions(AppUser user)
{
var entity = new UserActionEntity
{
UserId = user.Id,
};
await _context.tbl_UserActions.AddAsync(entity);
await _context.SaveChangesAsync();
return true;
}
I want to change the IsPasswordChanged field in the UserActions table to true when a user changes his/her password.
I am trying something like:
private async Task<bool> UpdateUserAction()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value; // gives me current user's id
var user = _context.tbl_UserActions
.Where(x => x.UserId.ToString() == userId).Select(x => x.IsPasswordChanged);
}
but I am not sure how to proceed and update this to "true". How do I update this entry?
You need to fetch the useraction entity from the table and then set the IsPasswordChanged property to true.
Try this:
private async Task<bool> UpdateUserAction()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value; // gives me current user's id
var user = _context.tbl_UserActions.FirstOrDefault(x => x.UserId.ToString() == userId);
if(user != null) //check if the record is not null
{
user.IsPasswordChanged = true; // set the column to desired value
_context.tbl_UserActions.Update(user);
await _context.SaveChangesAsync();
}
}

Signalr .net core 2.2 - mesages going to non connected groups

I am using .net core 2.2 with SignalR version 1.1.0. When I test the app, messages are being received by member who are NOT in the group. My groups are being dynamically created at run time based on relevant criteria, as in : var TheHub = CurrUser.Hubname; I cannot work out why group members who are NOT in the group are also receiving the messages. I am sending to GROUP and not ALL.
Please see code. Any help greatly appreciated, I am ready to pull my hair out.
My hub class
public class Chathub : Microsoft.AspNetCore.SignalR.Hub
{
public override async Task OnConnectedAsync()
{
var TheHub = CurrUser.Hubname;
await Groups.AddToGroupAsync(Context.ConnectionId, TheHub.ToString());
await base.OnConnectedAsync();
}
public Task SendMessageGroup(string user, string message)
{
var TheHub = CurrUser.Hubname;
return Clients.Group(TheHub.ToString()).SendAsync("ReceiveMessage", user, message);
}
}
My Javascript
"use strict";
document.getElementById("sendgroupButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessageGroup", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
playAudio();
});
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
document.getElementById("sendgroupButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendgroupButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
This is how I get the current value for curruser.hubname, please see below.
#inject SignInManager<ApplicationUser> SignInManager
#inject UserManager<ApplicationUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
CurrUser.CurrentUsertId = UserManager.GetUserId(User);
var ctx = new WebookContext();
var LoggedInGuestHouseName = (from Ghouse in ctx.Guesthouse
where Ghouse.UserId == CurrUser.CurrentUsertId
select Ghouse).SingleOrDefault();
//check to see if guesthouse details have been completed, if not skip this next line of code.
if( LoggedInGuestHouseName != null)
{
CurrUser.GuestHouseName = LoggedInGuestHouseName.GuestHouseName;
// add the hub to current user
CurrUser.HubId = (int) LoggedInGuestHouseName.HubId;
var Ghname = LoggedInGuestHouseName.GuestHouseName;
var GhUserEmailaddress = LoggedInGuestHouseName.Emailaddress;
var GhHuId = LoggedInGuestHouseName.HubId;
CurrUser.GuestHouseName = Ghname;
CurrUser.GuestHouseEmailaddress = GhUserEmailaddress;
var q = (from gh in ctx.Hub
where gh.HubId == GhHuId
select gh).SingleOrDefault();
var myhubname = q.HubName;
CurrUser.Hubname = myhubname;
};
}
Looks like SignalR core is not for the feint hearted. Until a authoritative book comes out, one is really walking blind. I have researched this topic blue, but alas have now given up on SignalR for now.