In config file i have
"HRServices": {
"CarService": "https://stackoverflow.com/Cars/...",
"EmployeesService": "https://stackoverflow.com/Employees/...",
"FinanceService": "https://stackoverflow.com/Finance/...",
....
}
Is it possible to specify base url for all those addresses?
To get something like
"baseUrl":"https://stackoverflow.com",
"HRServices": {
"CarService": "baseUrl/Cars/...",
"EmployeesService": "baseUrl/Employees/...",
"FinanceService": "baseUrl/Finance/...",
....
}
Update:
In controller it used:
private readonly IConfiguration _configuration;
public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public PartialViewResult Index()
{
string url = _configuration["HRServices:EmployeesService"];
...
}
using System.Collections.Generic;
namespace Microsoft.Extensions.Configuration
{
//
// Summary:
// Represents the root of an Microsoft.Extensions.Configuration.IConfiguration hierarchy.
public interface IConfigurationRoot : IConfiguration
{
//
// Summary:
// The Microsoft.Extensions.Configuration.IConfigurationProviders for this configuration.
IEnumerable<IConfigurationProvider> Providers { get; }
//
// Summary:
// Force the configuration values to be reloaded from the underlying Microsoft.Extensions.Configuration.IConfigurationProviders.
void Reload();
}
}
There's nothing that will do this for you automatically, but you can manually register your strongly typed config, and do it yourself:
services.AddScoped(p =>
{
var config = p.GetRequiredService<IConfiguration>();
var baseUrl = config["baseUrl"]
return new HRServicesConfig
{
CarService = baseUrl + config["HRServices:CarService"],
EmployeeService = baseUrl + config["HRServices:EmployeeService"],
FinanceService = baseUrl + config["HRServices:FinanceService"]
}
});
The one catch, is that you won't be using IOptions anymore, so you'd inject HRServicesConfig directly, rather than something like IOptions<HRServicesConfig>. Some might consider that a feature, though.
Related
I'm after the API that allows to apply [Authorize] attribute from API rather than having it on controller's class/method.
Consider third party controller classes that come from dependency nuget package and I want to host them in my application and implement security for them.
I know I can use
app.UseEndpoints(endpoints => { endpoints.MapControllers().RequireAuthorization(new AuthorizeAttribute("policy")); });
but this will configure ALL controllers and methods to be protected by policy and I need to have control per class/method.
Mapping manually with
endpoints.MapPut("***").RequireAuthorization(new AuthorizeAttribute("someOtherPolicy"))
is also not an option since the mapping is already defined with [Microsoft.AspNetCore.Mvc.HttpPost, Microsoft.AspNetCore.Mvc.Route("api/someRoute")] attributes on imported controllers.
Please advise
In case you can determinate on which endpoint should be specific authorization policy applied, you can try following:
public class MyAuthorizationFilter: AuthorizeAttribute, IAsyncAuthorizationFilter
{
public MyAuthorizationFilter() : base()
{
//this.AuthenticationSchemes = "";
//this.Policy = "";
//this.Roles = "";
}
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
context.Result = new OkObjectResult("my custom policy was checked");
return Task.CompletedTask;
}
}
public static class DynamicAuthorizationPolicies
{
public static void RegisterMyFilters(this IServiceProvider serviceProvider)
{
var allEndpoints = serviceProvider
.GetService<IActionDescriptorCollectionProvider>()
?.ActionDescriptors
?.Items;
if (allEndpoints is null)
{
return;
}
foreach (var endpoint in allEndpoints)
{
// If you debug hier you will see that you can register
// differnt filters or attribute in different collections
// for the endpoint
if (/*endpoint match some requirement*/true)
{
var authorizeFilter = serviceProvider.GetService<MyAuthorizationFilter>();
var descriptor = new FilterDescriptor(authorizeFilter, FilterScope.Action);
endpoint.FilterDescriptors.Add(descriptor);
}
}
}
}
... and here is the usage:
public void ConfigureServices(IServiceCollection services)
{
// services.Add...
services.AddTransient<MyAuthorizationFilter>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// app.Use....
app.ApplicationServices.RegisterMyFilters();
}
What I have learned is better store the sensitive data inside appSettings.json in .NET Core. I have an API address which I need to consume in my application, how should I store and call it from the appSettings.json? Can I store it as shown here?
"MyApi": {
"API": "http://example.com"
}
If I want to call it, how can I access it? If I don't want to use this approach, I should call it directly through HttpClient:
var client = newHttpClient()
client.BaseAddress = newUri("http://example.com");
try this
public class MyClass
{
private readonly string _myApi;
public MyClass(IConfiguration configuration)
{
myApi= configuration["MyApi:API"]);
}
}
after this you can use url anywhere inside of the class
But I highly recommend you to use an IHttpClientFactory
private readonly string _myApi;
private readonly IHttpClientFactory _clientFactory;
public MyClass(IConfiguration configuration, IHttpClientFactory clientFactory)
{
_myApi= configuration["MyApi:API"]);
_clientFactory = clientFactory;
}
public async Task Get ()
{
var httpClient = _clientFactory.CreateClient();
......
}
you have some way.
1- you can add httpClient in first step on start application and in controller or your services inject it with httpFactory
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("myApi", options =>
{
options.BaseAddress = new Uri(Configuration["MyApi:API"]);
});
//....
}
}
Controller or Your class
public class HomeController : Controller
{
private HttpClient apiClient;
public HomeController( IHttpClientFactory factory )
{
apiClient = factory.CreateClient("myApi");
}
public async Task<IActionResult> IndexAsync()
{
var resoult = await apiClient.GetAsync("/people/id/10");
//process your result
return View();
}
}
Update :
2 - inject Configuration to your controller or services like
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<IActionResult> IndexAsync()
{
var httpClient = new HttpClient();
httpClient.BaseAddress =new Uri(_configuration["MyApi:API"]);
var resoult = await httpClient.GetAsync("/people/id/10");
//process your result
return View();
}
}
I want to read appsettings.json non-controller class.Consider has a DatabaseUtil and contain a static connect() method. I need to connectionString for connection and i'm getting this from appsettings.json.This operation piece of cake in the startup.cs:)
Like this:
Configuration.GetConnectionString("HangfireDBConn")
Also it can be at the controller side with dependcy injection.But my problem which want to reach appSettings from DatbaseUtil class.
appSettings.json:
"NotifySettings": {
"DbConnection": "abc",
"Email": "abc#domain.com",
"SMTPPort": "5605"
}
Then i created my configuration settings class:
public class NotifySettings
{
public string DbConnection { get; set; }
public string Email { get; set; }
public string SMTPPort { get; set; }
}
And I added dependency for constructor injection to DatabaseUtil class and added IDatabaseUtil
public class DatabaseUtil : IDatabaseUtil
{
private static NotifySettings _NotifySettings;
public DatabaseUtil(IConfiguration _iconfig)
{
_NotifySettings = _iconfig.GetSection("NotifySettings").Get<NotifySettings>();
}
public static String ConnectToDatabase()
{
return "MESSAGE :" + _NotifySettings.DbConnection;
}
}
}
And i added DatabaseUtil to startup.cs
services.AddScoped<IDatabaseUtil, DatabaseUtil>();
and finally i injected IDatabaseUtil to my controller class and i can reach mysettings end of the this work.
Yes i can but not best way!
Let the join my Brain Storming :) ; If i have to inject to IDatabaseUtil every class where i want to use db helper methods.But if i had a static method in this class just it need to this line of code:
DatabaseUtils.connect();
That's feels me like i wrote unnecessary code.
What do you think about my approximation.Which one is best way for this case ?
change
services.AddScoped<IDatabaseUtil, DatabaseUtil>();
to
services.AddSingleton<IDatabaseUtil, DatabaseUtil>();
This way you only have one instance of DatabaseUtil
I'm still not entirely clear, but if the need here is to make values from your Configuration statically available, then copy them from your configuration to a static class during the startup:
public static class GlobalSettings
{
public static string ConnectionString { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
GlobalSettings.ConnectionString = Configuration.GetSection("ConnectionString").Value;
// ...
}
}
If you need to get the config and do the assignment from somewhere else, use the ConfigurationBuilder:
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
using System;
using Microsoft.Extensions.Configuration;
namespace project.Utility
{
public class ConnectionString
{
private IConfigurationRoot _config;
private static ConnectionString _internalInstance;
public static ConnectionString Instance
{
get
{
return _internalInstance;
}
}
public static void Init(IConfigurationRoot config)
{
_internalInstance = new ConnectionString();
_internalInstance._config = config;
}
public String Get(string key)
{
var NotifySettings =
Instance._config.GetSection(key).Get<NotifySettings>();;
return NotifySettings;
}
}
}
// call this above method from any place like controller or class file by below code
// use refernece of the namespace
ConnectionString connectionString = new ConnectionString(); // object creation
NotifySettings settings = connectionString.Get("NotifySettings"); // call with your key value get the settings object
Try this it should work let me know if any issues i can help on that
How to get absolute path in ASP net core alternative way for Server.MapPath
I have tried to use IHostingEnvironment but it doesn't give proper result.
IHostingEnvironment env = new HostingEnvironment();
var str1 = env.ContentRootPath; // Null
var str2 = env.WebRootPath; // Null, both doesn't give any result
I have one image file (Sample.PNG) in wwwroot folder I need to get this absolute path.
As of .Net Core v3.0, it should be IWebHostEnvironment to access the WebRootPath which has been moved to the web specific environment interface.
Inject IWebHostEnvironment as a dependency into the dependent class. The framework will populate it for you
public class HomeController : Controller {
private IWebHostEnvironment _hostEnvironment;
public HomeController(IWebHostEnvironment environment) {
_hostEnvironment = environment;
}
[HttpGet]
public IActionResult Get() {
string path = Path.Combine(_hostEnvironment.WebRootPath, "Sample.PNG");
return View();
}
}
You could go one step further and create your own path provider service abstraction and implementation.
public interface IPathProvider {
string MapPath(string path);
}
public class PathProvider : IPathProvider {
private IWebHostEnvironment _hostEnvironment;
public PathProvider(IWebHostEnvironment environment) {
_hostEnvironment = environment;
}
public string MapPath(string path) {
string filePath = Path.Combine(_hostEnvironment.WebRootPath, path);
return filePath;
}
}
And inject IPathProvider into dependent classes.
public class HomeController : Controller {
private IPathProvider pathProvider;
public HomeController(IPathProvider pathProvider) {
this.pathProvider = pathProvider;
}
[HttpGet]
public IActionResult Get() {
string path = pathProvider.MapPath("Sample.PNG");
return View();
}
}
Make sure to register the service with the DI container
services.AddSingleton<IPathProvider, PathProvider>();
.NET Core 3.0
Var 1:
string path = System.IO.Directory.GetCurrentDirectory();
Var 2:
string path = AppDomain.CurrentDomain.BaseDirectory.Substring(0, AppDomain.CurrentDomain.BaseDirectory.IndexOf("\\bin"));
.Net Core 3
For example I want to locate ~/wwwroot/CSS
public class YourController : Controller
{
private readonly IWebHostEnvironment _webHostEnvironment;
public YourController (IWebHostEnvironment webHostEnvironment)
{
_webHostEnvironment= webHostEnvironment;
}
public IActionResult Index()
{
string webRootPath = _webHostEnvironment.WebRootPath;
string contentRootPath = _webHostEnvironment.ContentRootPath;
string path ="";
path = Path.Combine(webRootPath , "CSS");
//or path = Path.Combine(contentRootPath , "wwwroot" ,"CSS" );
return View();
}
}
Some Tricks
Also if you don't have a controller or service,follow last Part and register it's class as a singleton.
Then, in Startup.ConfigureServices:
services.AddSingleton<your_class_Name>();
Finally, inject your_class_Name where you need it.
.Net Core 2
For example I want to locate ~/wwwroot/CSS
public class YourController : Controller
{
private readonly IHostingEnvironment _HostEnvironment;
public YourController (IHostingEnvironment HostEnvironment)
{
_HostEnvironment= HostEnvironment;
}
public ActionResult Index()
{
string webRootPath = _HostEnvironment.WebRootPath;
string contentRootPath = _HostEnvironment.ContentRootPath;
string path ="";
path = Path.Combine(webRootPath , "CSS");
//or path = Path.Combine(contentRootPath , "wwwroot" ,"CSS" );
return View();
}
}
MoreDetails
Thanks to #NKosi but IHostingEnvironment is obsoleted in MVC core 3!!
according to this :
Obsolete types (warning):
Microsoft.Extensions.Hosting.IHostingEnvironment
Microsoft.AspNetCore.Hosting.IHostingEnvironment
Microsoft.Extensions.Hosting.IApplicationLifetime
Microsoft.AspNetCore.Hosting.IApplicationLifetime
Microsoft.Extensions.Hosting.EnvironmentName
Microsoft.AspNetCore.Hosting.EnvironmentName
New types:
Microsoft.Extensions.Hosting.IHostEnvironment
Microsoft.AspNetCore.Hosting.IWebHostEnvironment : IHostEnvironment
Microsoft.Extensions.Hosting.IHostApplicationLifetime
Microsoft.Extensions.Hosting.Environments
So you must use IWebHostEnvironment instead of IHostingEnvironment.
* Hack *
Not recommended, but FYI you can get an absolute path from a relative path with
var abs = Path.GetFullPath("~/Content/Images/Sample.PNG").Replace("~\\","");
Prefer the DI/Service approaches above, but if you are in a non-DI situation (e.g., a class instantiated with Activator) this will work.
A better solution is to use the IFileProvider.GetFileInfo() method.
public IActionResult ResizeCat([FromServices] IFileProvider fileProvider)
{
// get absolute path (equivalent to MapPath)
string absolutePath = fileProvider.GetFileInfo("/assets/images/cat.jpg").PhysicalPath;
...
}
You must register IFileProvider like this to be able to access it through DI:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
var physicalProvider = _hostingEnvironment.ContentRootFileProvider;
var embeddedProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());
var compositeProvider = new CompositeFileProvider(physicalProvider, embeddedProvider);
// choose one provider to use for the app and register it
//services.AddSingleton<IFileProvider>(physicalProvider);
//services.AddSingleton<IFileProvider>(embeddedProvider);
services.AddSingleton<IFileProvider>(compositeProvider);
}
As you can see this logic (for where a file comes from) can get quite complex, but your code won't break if it changes.
You can create a custom IFileProvider with new PhysicalFileProvider(root) if you have some special logic. I had a situation where I want to load an image in middleware, and resize or crop it. But it's an Angular project so the path is different for a deployed app. The middleware I wrote takes IFileProvider from startup.cs and then I could just use GetFileInfo() like I would have used MapPath in the past.
If you are accessing it from Startup/Program routines, it is available under WebApplicationBuilder.Environment e.g.
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var webRootPath = builder.Environment.WebRootPath;
ConfigureServices(builder);
...
}
In one of my concrete class. I have the method.
public class Call : ICall
{
......
public Task<HttpResponseMessage> GetHttpResponseMessageFromDeviceAndDataService()
{
var client = new HttpClient();
var uri = new Uri("http://localhost:30151");
var response = GetAsyncHttpResponseMessage(client, uri);
return response;
}
Now I put the url into appsettings.json.
{
"AppSettings": {
"uri": "http://localhost:30151"
}
}
And I created a Startup.cs
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
}
and now I get stuck.
EDIT
By the way, I don't have a controller, it is a console application.
The preferred way to read configuration from appSettings.json is using dependency injection and the built or (or 3rd party) IoC container. All you need is to pass the configuration section to the Configure method.
public class AppSettings
{
public int NoRooms { get; set; }
public string Uri { get; set; }
}
services.Configure<AppSettings>(Configuration.GetSection("appsettings"));
This way you don't have to manually set the values or initialize the AppSettings class.
And use it in your service:
public class Call : ICall
{
private readonly AppSettings appSettings;
public Call(IOptions<AppSettings> appSettings)
{
this.appSettings = appSetings.Value;
}
public Task<HttpResponseMessage>GetHttpResponseMessageFromDeviceAndDataService()
{
var client = new HttpClient();
var uri = new Uri(appSettings.Uri);
var response = GetAsyncHttpResponseMessage(client, uri);
return response;
}
}
The IoC Container can also be used in a console application, you just got to bootstrap it yourself. The ServiceCollection class has no dependencies and can be instantiated normally and when you are done configuring, convert it to an IServiceProvider and resolve your main class with it and it would resolve all other dependencies.
public class Program
{
public static void Main(string[] args)
{
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
var configuration = configurationBuilder.Build()
.ReloadOnChanged("appsettings.json");
var services = new ServiceCollection();
services.Configure<AppSettings>(configuration.GetSection("appsettings"));
services.AddTransient<ICall, Call>();
// add other services
// after configuring, build the IoC container
IServiceProvider provider = services.BuildServiceProvider();
Program program = provider.GetService<Program>();
// run the application, in a console application we got to wait synchronously
program.Wait();
}
private readonly ICall callService;
// your programs main entry point
public Program(ICall callService)
{
this.callService = callService;
}
public async Task Run()
{
HttpResponseMessage result = await call.GetHttpResponseMessageFromDeviceAndDataService();
// do something with the result
}
}
Create a static class
public static class AppSettings
{
public static IConfiguration Configuration { get; set; }
public static T Get<T>(string key)
{
if (Configuration == null)
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
var configuration = builder.Build();
Configuration = configuration.GetSection("AppSettings");
}
return (T)Convert.ChangeType(Configuration[key], typeof(T));
}
}
then access the settings anywhere you want like
var uri = AppSettings.Get<string>("uri");
var rooms = AppSettings.Get<int>("noRooms");
appsettings.json example
{
"AppSettings": {
"uri": "http://localhost:30151",
"noRooms": 100
}
}
You can access data from the IConfigurationRoot as following:
Configuration["AppSettings:uri"]
Like suggested in the comment I would put the information in a seperate class for that info and pass it into the DI container.
the class
public class AppSettings {
public string Uri { get; set; }
}
DI
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(new AppSettings() { Uri = Configuration["AppSettings:uri"] });
// ...
}
Controller
public class DemoController
{
public HomeController(IOptions<AppSettings> settings)
{
//do something with it
}
}