Get database record based on id - asp.net-core

Greeting,
I have an website that display products everything okay until now but i want the current user that have logged in to get his/ her data only, eg. if the user has id=1 its will get all products will id=1 (i have foreign key constrain between the two tables) i am using very simple controller that only get all products, i didn't find any solution at google.
i am using .NET CORE 3.0 with EntityFramework and SQL SERVER database
i am currently using JWT token that is stored inside local storage at web browser.
here is my ProductsController
public class ProductsController : ControllerBase
{
private readonly IAuthRepository _repo;
private readonly DataContext _context;
public ProductsController(DataContext context, IAuthRepository repo)
{
_context = context;
_repo = repo;
}
[HttpGet]
public async Task<IActionResult> AllProducts()
{
var All = await _context.Product.ToListAsync();
return Ok(All);
}

below is the code to get userid from jwt token
var context = new HttpContextAccessor();
var principal = context.HttpContext.User;
if (null == principal)
return null;
var id = principal.FindFirstValue(ClaimTypes.NameIdentifier);
Then update your action method " AllProducts" to
[HttpGet]
public async Task<IActionResult> AllProducts()
{
var context = new HttpContextAccessor();
var principal = context.HttpContext.User;
if (null == principal)
return null;
var userid = principal.FindFirstValue(ClaimTypes.NameIdentifier);
var All = await _context.Product.where(p=>p.id==userid).ToListAsync();
return Ok(All);
}

Related

Cannot convert from System.security.claims.claimsPrincipal to Scheduler.Areas.Identity.Data

I'm trying to get the current user ID so my Events index page will show events from only current user.
my HttpContext.User has the error "Cannot convert from System.security.claims.claimsPrincipal to Scheduler.Areas.Identity.Data" inside the index of my Events controller.
var userId = await _userManager.GetUserIdAsync(HttpContext.User);
Here is the code in my EventsController:
public EventsController(SchedulerDbContext context, UserManager<SchedulerUser> userManager)
{
_context = context;
_userManager = userManager;
}
// GET: Events
[Authorize]
public async Task<IActionResult> Index()
{
var userId = await _userManager.GetUserIdAsync(HttpContext.User);
return View(await _context.Events.Where(g => g.SchedulerUser == userId).ToListAsync());
}
Where should I be looking to solve this? I'll update with more code if required.
GetUserIdAsync only accepts an IdentityUser type. It doesn't accept a type of ClaimsPrincipal, but GetUserAsync does.
var user = await _userManager.GetUserAsync(HttpContext.User);
var userId = user.Id;
Alternatively, you could also get the Id claim from claimsidentity.
var userId = claimsIdentity.FindFirst("id")?.Value;

Blazor server scoped service is different

I need to store the authenticated user Session Id, where access token is retrieved from dictionary per user session.
I have simple Scoped service services.AddScoped<SessionService>();
public class SessionService
{
public string SessionId { get; set; }
}
In MainLayout.razor I'm injecting it and setting it when the user log in. Which works fine.
#inject SessionService SessionService
.....
#code {
[CascadingParameter]
private Task<AuthenticationState> AuthenticationStateTask { get; set; }
protected override async Task OnInitializedAsync()
{
SessionService.SessionId = (await AuthenticationStateTask).User.GetSessionId();
}
}
However, I'm creating HttpClient from IHttpClientFactory and getting the access token based on the user Session Id, but all 3 different approaches to get the SessionService have SessionId set to null
services.AddHttpClient("backend", async (provider, client) =>
{
var httpContextAccessor = provider.GetService<IHttpContextAccessor>();
var httpContextService = httpContextAccessor.HttpContext.RequestServices.GetService<SessionService>();
using var scope = provider.CreateScope();
var anotherProvider = services.BuildServiceProvider();
var anotherSession = anotherProvider.GetService<SessionService>();
var sessionId = scope.ServiceProvider.GetService<SessionService>()?.SessionId;
client.BaseAddress = ...;
});
If I use the service inside component the SessionId is what it has to be and not null. Why is that happening and how to fix it?

Create a Helper class from a Controller's method thats getting fat

This method receive a CSV file (Sale, Date) POSTed from front-end, cleans a table and inserts csv file record into the table, then gets latest Date and returns it to the front-end. I know the code looks a bit ugly and I have a lot to learn but what I am trying to figure out here is how to create a Helper class from this code since the method is getting too fat I gess?
So I tried to migrate some of the code to a Helper class then I created a static Class but the problem is that I couldn't inject dependencies into its constructor since it was an static Class... then no database service from the Helper class and this is not too "helper".
So in your opinion, is this method too long/ fat?
Is there a need for a Helper Class?
How can I build it?
Cheers
Here my Controller
private IDataService<Sale> _SaleDataService;
private readonly MyOptions _myOptions;
public DateTime LastWindowDay;
public ForecastApiController(IDataService<Sale> service, IOptions<MyOptions> optionsAccessor)
{
_SaleDataService = service;
_myOptions = optionsAccessor.Value;
}
[HttpPost("api/Sales/uploadFile")]
public async Task<IActionResult> UploadFiles()
{
try
{
//clean table
var all = _SaleDataService.GetAll();
if (all.Count() > 0)
{
foreach (var item in all)
{
_SaleDataService.Delete(item);
}
}
var files = Request.Form.Files;
//Read Request for the unique uploaded file (method can process multiple but frontend will post just one)
foreach (var file in files)
{
using (System.IO.StreamReader sr = new StreamReader(file.OpenReadStream()))
{
//Reads the file one line at a time until its end
String line = await sr.ReadLineAsync();
while (sr.Peek() >= 0)
{
//Split the values on the line and convert them into a propper format
var fileLine = sr.ReadLine();
var lineToArray = fileLine.Split(',').ToArray();
var timeElement = DateTime.Parse(lineToArray[0]);
var saleAmauntElement = float.Parse(lineToArray[1], System.Globalization.CultureInfo.InvariantCulture);
//Discard negative values and store data into database
if (saleAmauntElement >= 0)
{
//Store line into database
Sale sl = new Sale
{
SaleDate = timeElement,
SaleAmount = saleAmauntElement
};
_SaleDataService.Create(sl);
LastWindowDay = sl.SaleDate;
}
}
// Simple Moving Average method will be used and the restriction is that it will calculate only within a week after -
// - last day of historical data.
// Create an array and stores the next 7 days from the last day that there is data
string[] predictionDays = new string[7];
for (int i = 0; i < 7; i++)
{
predictionDays[i] = LastWindowDay.AddDays(i + 1).ToString("dd/MM/yyyy");
}
//returns the array to frontend to let the user select a prediction day
return Json(predictionDays);
}
}
return Json(new { message = "Error trying to process information" });
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { message = ex.Message });
}
}
Following the Repository pattern helps to maintain the controllers and the project in general, it is like having a helper class with all your operations (business logic and DB).
In your Startup.cs
services.AddScoped<IRepository, Repository>();
Create an Interface
public interface IRepository
{
IEnumerable<MyData> GetData();
}
Create your helper class
public partial class Repository : IRepository
{
private DBContext _context;
private ILogger<Repository> _logger;
private IHttpContextAccessor _httpContextAccessor;
public Repository(DBContext context, ILogger<Repository> logger, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
public async Task<bool> SaveChangesAsync()
{
return (await _context.SaveChangesAsync()) > 0;
}
public IEnumerable<MyData> GetData()
{
_logger.LogInformation("Getting All Data from the Database");
return _context.Data.ToList();
}
}
Finally inject it in your Controller
public class RequestsController : Controller
{
private IRepository _repository;
private ILogger<RequestsController> _logger;
private IConfiguration _config;
public RequestsController(IRepository repository,ILogger<RequestsController> logger,IConfiguration config)
{
_repository = repository;
_logger = logger;
_config = config;
}
// GET: Requests
public IActionResult Index()
{
var data = _repository.GetData()
return View(data);
}
}
The best practice approach is to follow the repository pattern as advised by Steve Tolba's answer. The repository pattern alleviates the bulk of query logic, transformation of view Models and the calling of business models from the controller.
However, if for whatever reason you do not want to follow the repository pattern and just want to split up your controller, you may pass the reference to the controller as a parameter to another action method:
[HttpGet("myActionMethodThatUsedToBeFat")]
public int MyActionMethodThatUsedToBeFat()
{
await HelperAction(this);
//Do stuff...
}
private async Task<byte[]> HelperAction(Controller controller)
{
//Do stuff in here that would have made the calling action method too bulky
}

ASP.NET 5 (MVC6) How to seed users

How would you seed users? I am following their documents here, but they only show how to seed data that is inserted directly by the ApplicationDbContext.
In the Account controller, the UserManager is created through DI. How would I instantiate a UserManager in the Seed method?
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService<ApplicationDbContext>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
Then in Startup.cs in the Configure method:
SeedData.Initialize(app.ApplicationServices);
In the startup.cs in the configure method, you can add the following code to get the UserManager and from there seed the users.
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
You would create a class in the lines of
public class UserSeed
{
private ApplicationDbContext _context;
private UserManager<ApplicationUser> _mgr;
public UserSeed(ApplicationDbContext context,UserManager<ApplicationUser> userManager)
{
_context = context;
_mgr = users;
}
public void UserSeedData()
{
var user = new ApplicationUser { UserName = "foo#foo.com", Email = "foo#foo.com" };
var result = _mgr.CreateAsync(user,"fooPassword");
......
......
}
}
In the configure method of your startup.cs take userSeed through DI like
public async void Configure(...., UserSeed userSeedData)
and then inside the method you call
userSeedData.UserSeedData();
Don't use this code as is, you would probably want to check if the user already exists before you create it.
Note: This method will run once every time your application starts.

Uni Test for Log in Page giving null

i am writing a unitest for login in my project .
when i call the login function of my controller. Memership.GetUser giving null value for passed User.
below is Test case
[TestMethod]
public void Login()
{
//Arrange
AccountController account = new AccountController(_forgotPasswordTokensRepo, _IMessageTemplateDAO, _IEmailService, _ISettingDAO, _IProfileDAO);
List<AccountBO> TestUsers = new List<AccountBO>();
AccountBO objAccountBO = new AccountBO();
objAccountBO.Email = "uu#yopmail.com";
objAccountBO.Password = "123456789";
TestUsers.Add(objAccountBO);
var result = (JsonResult)account.Login(TestUsers[0]);
var json = new System.Web.Script.Serialization.JavaScriptSerializer();
string data = result.Data.ToString().Split('=')[1].Trim();
bool Processdata = Convert.ToBoolean(data.Replace('}', ' ').Trim());
Assert.AreEqual<bool>(true, Processdata);
}
Controller function is
public JsonResult Login(AccountBO account, string returnUrl = "")
{
bool hasBeenUnlocked = false;
if (ModelState.IsValid)
{
MembershipUser adminUser;
adminUser = Membership.GetUser(account.Email);
------so on
}
}
here adminUser = Membership.GetUser(account.Email) is giving null.
What do you expect GetUser to return? Are you expecting it to query the db/active directory and return a user with full credentials?
Unless I remember wrong, Membership is part of Asp.Net infrastructure so it is not set up in the context of your unit test. The solution is not to set it up. It is to stub out that functionality.
public interface IProvideUsers {
public MembershipUser Get(string email, string password);
}
public class AspNetMembershipProvider : IProvideUsers {
public MembershipUser Get(string email) {
//...
}
}
then in your controller
IProvideUsers users;
....
users.Get(account.Email, account.Password);
where users is injected via the constructor. Then in your tests you create your own implmentation of IProvideUsers and provide that.