Asp.Net Core 2 Web API 500 internal server error - asp.net-core

when I try to access the API via Postman,
Send:
localhost:5050/api/Auth/token
Body:
{ "UserName": "jouverc", "Password": "P#ssw0rd!" }
to this method:
[Produces("application/json")]
[Route("api/Auth")]
public class AuthController : Controller
{
#region constructor injection
private readonly IPasswordHasher<User> _hasher;
private readonly UserManager<User> _userManager;
private readonly IConfigurationRoot _config;
private readonly SignInManager<User> _signInManager;
public AuthController(IPasswordHasher<User> hasher, UserManager<User> userManager, SignInManager<User> signInManager, IConfigurationRoot config)
{
_hasher = hasher;
_userManager = userManager;
_signInManager = signInManager;
_config = config;
}
#endregion
#region createToken
[HttpPost("token")]
public async Task<IActionResult> CreateToken([FromBody] CredentialModel model)
{
try
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null)
{
if (_hasher.VerifyHashedPassword(user, user.PasswordHash, model.Password) == PasswordVerificationResult.Success)
{
return Ok(CreateToken(user));
}
}
}
catch (Exception)
{
//log
}
return null;
}
private async Task<JwtPacket> CreateToken(User user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())
}.Union(userClaims);
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(issuer: _config["Tokens:Issuer"],
audience: _config["Tokens:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(2),
signingCredentials: cred
);
return new JwtPacket
{
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo.ToString(),
UserName = user.UserName
};
}
public class JwtPacket
{
public string Token;
public string UserName;
public string Expiration;
}
#endregion
}
I receive a 500 Internal Server Error:
Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfigurationRoot' while attempting to activate 'WebAPI.Controllers.AuthController
how should i configurate the Startup?
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.

In .net core 2.0
IConfigurationRoot is now just IConfiguration.
Explained in this document: Migrating from 1.x to 2.0.
In 2.0 projects, the boilerplate configuration code inherent to 1.x projects runs behind-the-scenes. For example, environment variables and app settings are loaded at startup. The equivalent Startup.cs code is reduced to IConfiguration initialization with the injected instance:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }

Just change IConfigurationRoot to IConfiguration on the constructor for the controller.
But what might be better is to use the IOpions pattern for injecting settings into your controller.

If you are just reading values from the appsettings.json then use the IConfiguration interface instead.
Here's how to implement it in the Startup class
public Startup(IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddEnvironmentVariables()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
}

Related

How to inject custom service on startup in .NET Core 5

I want to read my data from database and control it, and I need to do this in the request pipeline at startup.
So I have to do dependency injection at startup.
This is my (DI)
public Startup(IConfiguration configuration,IAuthHelper authHelper)
{
Configuration = configuration;
AuthHelper = authHelper;
}
public IConfiguration Configuration { get; }
public IAuthHelper AuthHelper;
I encounter this error
An error occurred while starting the application.
InvalidOperationException: Unable to resolve service for type 'Laboratory.Core.Services.Interfaces.IAuthHelper' while attempting to activate 'Laboratory.Startup'.
I used service like this
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var siteDirectory = AuthHelper.GetSiteSetting().MediaPath;
var fileServerOptions = new FileServerOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine
(env.WebRootPath, $#"{siteDirectory}User Picture\")),
RequestPath = "/ServerFiles"
};
app.UseFileServer(fileServerOptions);
}
This is my service
public class AuthHelper : IAuthHelper
{
private readonly LaboratoryContext _context;
private readonly IRazorPartialToStringRenderer _renderer;
private readonly IHttpContextAccessor _httpContext;
private readonly IHttpClientFactory _clientFactory;
public AuthHelper(LaboratoryContext context, IRazorPartialToStringRenderer renderer, IHttpContextAccessor httpContext, IHttpClientFactory clientFactory)
{
_context = context;
_renderer = renderer;
_httpContext = httpContext;
_clientFactory = clientFactory;
}
public TableSiteSetting GetSiteSetting()
{
try
{
return _context.TableSiteSettings.AsNoTracking().FirstOrDefault();
}
catch (SqlException)
{
return new TableSiteSetting() { StaticIp = "ServerConnectionError" };
}
catch (Exception)
{
return new TableSiteSetting() { StaticIp = "ServerError" };
}
}
}
Thanks for any help.
Your service can't be injected in Startup constructor because it has not been added yet to the dependency injection container. But you can inject it to the Configure method.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAuthHelper authHelper)
{
...
}
I assume you have already registered the service in ConfigureServices
services.AddSingleton<IAuthHelper, AuthHelper>(); // Or scoped/transient depends what your service does.
You can get dbcontext service in program.cs and do what ever you like to your database data.
for example I use this approach to seed my database:
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
await ApplicationDbContextSeed.SeedSampleDataAsync(context)
}
host.Run();

Blazor : How to read appsetting.json from a class in .NET 6?

The following is working for me, but not sure this is the right way to do use DI in .NET6 blazor.
I have the following class
public class Authentication
{
private IConfiguration _configuration;
private AppState _appState;
public Authentication(IConfiguration Configuration, AppState appState)
{
_configuration = Configuration;
_appState = appState; ;
}
public async Task<AccessToken?> getAccessToken()
{
var tokenServer = _configuration.GetValue<string>("tokenUrl");
var clientID = _configuration.GetValue<string>("ABC:ClientID");
var clientSecret = _configuration.GetValue<string>("ABC:ClientSecret");
var grantType = _configuration.GetValue<string>("ABC:GrantType");
AccessToken? accessToken = null;
.............
............
return accessToken;
}
}
in my code behind of razor page
namespace XXXXXXXXXXX.Pages
{
public partial class Index
{
[Inject]
public ILogger<Index> _Logger { get; set; }
[Inject]
public IConfiguration Configuration { get; set; }
[Inject]
public AppState _appState { get; set; }
**Authentication auth;**
protected override void OnInitialized()
{
**auth = new Authentication(Configuration, _appState);**
base.OnInitialized();
}
private async Task HandleValidSubmit()
{
_Logger.LogInformation("HandleValidSubmit called");
auth.getAccessToken();
// Process the valid form
}
}
}
My Question is I was Expecting the DI to do its magic and Insert the Dependency in my class.
but to get this working i had to write
auth = new Authentication(Configuration, _appState);
I was expecting to instantiate
using auth = new Authentication() , but this one throws compiler error.

Create database context from cookie and base path in Entity Framework Core

Postgres database has multiple schemes like company1, company2, ... companyN
Browser sends cookie containing scheme name . Data access operations should occur in this scheme. Web application user can select different scheme. In this case different cookie value is set.
Npgsql EF Core Data provider is used.
ASP NET MVC 5 Core application registers factory in StartUp.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IEevaContextFactory, EevaContextFactory>();
....
Home controller tries to use it:
public class HomeController : EevaController
{
public ActionResult Index()
{
var sm = new SchemeManager();
sm.PerformInsert();
....
This throws exception since factory member is null. How to fix this ?
public interface IEevaContextFactory
{
EevaContext Create();
}
public class EevaContextFactory : IEevaContextFactory
{
private IHttpContextAccessor httpContextAccessor;
private IConfiguration configuration;
public EevaContextFactory(IHttpContextAccessor httpContextAccessor, IConfiguration configuration)
{
this.httpContextAccessor = httpContextAccessor;
this.configuration = configuration;
}
public EevaContext Create()
{
var builder = new DbContextOptionsBuilder<EevaContext>();
var pathbase = httpContextAccessor.HttpContext.Request.PathBase.Value;
var scheme = httpContextAccessor.HttpContext.Request.Cookies["Scheme"];
var csb = new NpgsqlConnectionStringBuilder()
{
Host = pathbase,
SearchPath = scheme
};
builder.UseNpgsql(csb.ConnectionString);
return new EevaContext(builder.Options);
}
}
Scheme data acess methods:
public class SchemeManager
{
readonly IEevaContextFactory factory;
public SchemeManager(IEevaContextFactory factory)
{
this.factory = factory;
}
public SchemeManager()
{
}
public void PerformInsert()
{
using (var context = factory.Create())
{
var commandText = "INSERT into maksetin(maksetin) VALUES (CategoryName)";
context.Database.ExecuteSqlRaw(commandText);
}
}
}
var sm = new SchemeManager()
... will call the no-parameter constructor on SchemeManager so the IEevaContextFactory is not injected. You should inject your factory into your controller and pass it into your SchemeManager.
Remove your no-parameter constructor. It's not needed.
public class HomeController : EevaController
{
private IEevaContextFactor eevaFactory;
public HomeController(IEevaContextFactory factory)
{
eevaFactory = factory;
}
public ActionResult Index()
{
var sm = new SchemeManager(eevaFactory);
sm.PerformInsert();
....
}
}
Your other option is to put the SchemeManager in the DI container and then the DI container will auto-resolve IEevaContextFactory on the constructor and then just inject SchemeManager into your controller.
Either way, remove that no-parameter constructor.

IdentityServer4 Reject Token Request If Custom Parameter Not Valid

I have this test client sending RequestToken:
var tokenResponse = await client.RequestTokenAsync(new TokenRequest
{
Address = disco.TokenEndpoint,
GrantType = "password",
ClientId = "My_Client",
ClientSecret = "mysecret",
Parameters =
{
{ "username", "user#entity.com" },
{ "password", "userpassword" },
{ "logged_entity_id", "143" },
{ "scope", "MyAPI" }
}
});
Now each user has a list of entity and I want to reject the token request if the value in the parameter "logged_entity_id" does not exist in the user's list of entity.
I was initially planning on checking it via IsActiveSync in my CustomProfileService but I can't seem to access the raw parameters in IsActiveSync method.
public class CustomProfileService : IProfileService
{
protected UserManager<User> _userManager;
public CustomProfileService(UserManager<User> userManager)
{
_userManager = userManager;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var claims = new List<Claim>
{
new Claim("LoggedEntityId", context.ValidatedRequest.Raw["logged_entity_id"])
};
context.IssuedClaims.AddRange(claims);
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
var user = _userManager.GetUserAsync(context.Subject).Result;
// var entityId = Can't access logged_entity_id parameter here
context.IsActive = user != null && user.DeletingDate == null && user.entities.Contains(entityId);
return Task.FromResult(0);
}
}
I'm not really sure if this is where I should check and reject it.
In asp.net core you can register a dependency using the built-in dependency injection container. The dependency injection container supplies the IHttpContextAccessor to any classes that declare it as a dependency in their constructors:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
Then in your class ,for example , in the implement of IProfileService :
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomProfileService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Then in IsActiveAsync method get the value by :
var id = _httpContextAccessor.HttpContext.Request.Form["logged_entity_id"].ToString();
You can implement ICustomTokenValidator to validate token's request on your own way
You can run custom code as part of the token issuance pipeline at the token endpoint. This allows e.g. for
adding additional validation logic
changing certain parameters (e.g.token lifetime) dynamically
public class CustomValidator : ICustomTokenRequestValidator
{
public Task<TokenValidationResult> ValidateAccessTokenAsync(TokenValidationResult result)
{
throw new NotImplementedException();
}
public Task<TokenValidationResult> ValidateIdentityTokenAsync(TokenValidationResult result)
{
throw new NotImplementedException();
}
}
and in your startup.cs:
services.AddIdentityServer(options =>
{
...
})
.AddCustomTokenRequestValidator<CustomValidator>();

In ASP.NET Core 2.2, How To Get Base URL in startup service

I've got an asp.net core 2.2 project with a startup.cs service that does an async REST GET call back to my current site and then returns the result (View Component using DI) back to the razor view.
In the service, I want to call "/api/sessions" and not "http://localhost:3433/api/sessions". I know I could use the ~ ta helper if I were inside my razor page to get the base path to the web server, but how can I get that from a service?
Here is my service and relevant code.
From: SessionsService.cs (this is where I don't want http://localhost but just ~/
public class Session
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
public class SessionsService : ISessionsService
{
public async Task<List<Session>> GetSessions(int speakerId)
{
var uri = new Uri("http://localhost:50463/api/sessions");
var httpClient = new HttpClient();
var result = await httpClient.GetStringAsync(uri);
var sessions = JsonConvert.DeserializeObject<List<Session>>(result);
return sessions;
}
}
From: startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISessionsService, SessionsService>();
From: index.cshtml
<vc:speaker-card speaker="#speaker" ></vc:speaker-card>
From: SpeakerCardViewComponent.cs
{
private ISessionsService _sessionsService;
public SpeakerCardViewComponent(ISessionsService sessionsService)
{
_sessionsService = sessionsService;
}
public async Task<IViewComponentResult> InvokeAsync(
Speaker speaker)
{
var sessions = await _sessionsService.GetSessions(101);
speaker.Sessions = sessions;
return View(speaker);
}
}
ANSWER AS SUGGESTED BY KIRK LARKIN FOLLOWS:
public async Task<List<Session>> GetSessions(int speakerId,string baseUrl)
{
var uri = new Uri($"{baseUrl}api/sessions");
var httpClient = new HttpClient();
var result = await httpClient.GetStringAsync(uri);
var sessions = JsonConvert.DeserializeObject<List<Session>>(result);
return sessions;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISessionsService, SessionsService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
public class SpeakerCardViewComponent : ViewComponent
{
private ISessionsService _sessionsService;
private IHttpContextAccessor _httpContextAccessor;
public SpeakerCardViewComponent(ISessionsService sessionsService, IHttpContextAccessor httpContextAccessor)
{
_sessionsService = sessionsService;
_httpContextAccessor = httpContextAccessor;
}
public async Task<IViewComponentResult> InvokeAsync(
Speaker speaker)
{
var isHttps = _httpContextAccessor.HttpContext.Request.IsHttps;
var baseUrl = isHttps ? "https://" : "http://"
+ _httpContextAccessor.HttpContext.Request.Host.Value
+ "/";
var sessions = await _sessionsService.GetSessions(speaker.SpeakerId, baseUrl);
speaker.Sessions = sessions;
return View(speaker);
}
}