In my repository class, I have my Config object and looks like my connection string is under:
Config > Providers > Microsoft.Configuration.Json.JsonConfigurationProvider > Data > ConnectionStrings.myConnectionString
This is what my appsettings.json looks like:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"myConnectionString": details..."
}
}
I'm trying to read myConnectionString as follows which is not working:
var cs = _config.GetSection("ConnectionStrings.myConnectionString").value;
What am I doing wrong?
UPDATE:
For some reason, I'm not seeing GetValue() method. I'm using ASP.NET Core 2.0.
Configuration API provides extension method for IConfiguration to simplify reading ConnectionStrings section:
// using Microsoft.Extensions.Configuration;
string connectionString = _config.GetConnectionString("myConnectionString");
what it does is return configuration?.GetSection("ConnectionStrings")?[name];
The issue seems to lie in the string path you are passing to the GetSection() method. According to the ASP.NET Core Configuration documentation you should use "ConnectionStrings:myConnectionString" instead of "ConnectionStrings.myConnectionString".
Plus, if you wish to retrieve the value directly you may prefer to use the GetValue() method instead:
var cs = _config.GetValue("ConnectionStrings:myConnectionString", "");
If you prefer, you can also use the index notation as:
var cs = _config["ConnectionStrings:myConnectionString"];
But I honestly find the first approach more clean and elegant as the GetValue() method allows you to specify a default value in case the property is not found in the configuration.
Related
I had always imagined that
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
would set my project code's log level to Debug, and make Microsoft.* namespaces log at Information level or higher. It seems not. With this config, AspNetCore infrastructure logs at Debug level.
How do I target Microsoft.* to not use the Default level?
I'm using Serilog, but the injected Loggers are all Microsoft.Extensions.Logging.ILogger so I expected Microsoft.Extensions.Logging config to kick in.
Is this a mistaken assumption?
Yes, the assumption that Serilog will respect the Logging.LogLevel section when used with Extensions.Logging is wrong. It doesn't.
To read serilog levels from config, some options are:
use https://github.com/serilog/serilog-settings-configuration and use the config structure they give, especially the section at MinimumLevel, LevelSwitches, overrides
For those who do Serilog config in code, use something like this:
var defaultLogLevel = configuration.GetLogLevel("Default");
var aspNetCoreLogLevel = configuration.GetLogLevel("Microsoft.AspNetCore");
var microsoftLogLevel = configuration.GetLogLevel("Microsoft");
var logger = new LoggerConfiguration()
.MinimumLevel.Is(defaultLogLevel)
.MinimumLevel.Override("Microsoft.AspNetCore", aspNetCoreLogLevel)
.MinimumLevel.Override("Microsoft", microsoftLogLevel)
// ... etc ...
// ...
static LogEventLevel GetLogLevel(this IConfiguration configuration, string #namespace, string fallbackLevel = "Information")
{
return Enum.Parse<LogEventLevel>(configuration["Logging:LogLevel:" + #namespace] ?? fallbackLevel);
}
but the in-config approach for minimum levels has the advantage that you can make use of reloadOnChange
I am looking into Stored Cross-site Scripting vulnerabilities that occur when the data provided by an attacker is saved on the server, and is then displayed upon subsequent requests without proper HTML escaping.
I have NET 5 ASP.NET Core application using MVC. The application is using jQuery and Telerik's ASP.NET Core library. Both use JSON data returned from the server.
The application has several action methods that query stored data in the database and return as JsonResult.
For example, the following action method
[HttpGet]
[Route("items/json/{id}")]
public async Task<ActionResult> GetName([FromRoute] int id)
{
var i = await _itemService.GetWorkItem(id);
return Json(new
{
ItemName = i.Name
});
}
and client side script shows the ItemName in html using jQuery
$.get(url)
.done(function (response, textStatus, jqXHR) {
$("#itemname").html(response);
})
Suppose a user has stored the name as <script>alert('evil');</script> then the code above will execute the evil script on client side.
The application is using Newtonsoft as default serializer. By default the response does not get Html encoded. The response from the server looks like
{"ItemName":"\u003Cscript\u003Ealert(\u0027evil\u0027);\u003C/script\u003E"}
Also setting default JsonSerializerSettings in Startup like below does not work the same way as the Html Encode.
var serializerSettings = new JsonSerializerSettings()
{
StringEscapeHandling = StringEscapeHandling.EscapeHtml
};
Is there any default way in ASP.NET Core (Net 5) to handle html encoding during JSON serialization?
I understand that there is WebUtility.HtmlEncode() and also HtmlEncoder class available which can be used to apply encoding selectively . I am looking for a solution to handle html encoding by default during the JSON serialization.
Is new System.Text.Json by default applies html encoding on property values?
UPDATE 1
The comments below suggest to configure NewtonsoftJson in startup.cs. Note that question is NOT how to configure newtonsoft globally but how to html encode property value during the serialization so client (Browser) wont execute the malicious script.
I have tried Newtonsoft.Json.StringEscapeHandling.EscapeHtml which did not work. The script still executes
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddNewtonsoftJson((options) =>
{
options.SerializerSettings.StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml;
});
}
You have to use Newtonsoft.Json if you don't want to create tons of code for each quite simple case. This is working for me
[HttpGet]
public async Task<ActionResult> MyTest ()
{
return new JsonResult(new
{
ItemName = "<script> alert('evil');</script>"
});
}
and use response.itemName on client side
$("#itemname").html(response.itemName);
to use Newtonsoft.Json change your startup code to this
using Newtonsoft.Json.Serialization;
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver());
I'm using ASP.NET Core 5, and Swagger. I know how to use Swagger, and it works properly.
Swagger is served on foo:5001/swagger - but I need to determine that URL programmatically at runtime.
How can I do that?
I already tried:
Getting it by injecting IEnumerable<EndpointDataSource> into some helper/controller class, but that shows me all routes EXCEPT swagger's.
Getting it while setting up endpoint routing and inspecting IEndpointRouteBuilder, but once again it shows me all routes EXCEPT swagger's.
According to sources at https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs you can use an instance of class SwaggerUIOptions:
Register instance in DI container:
var options = new SwaggerUIOptions
{
RoutePrefix = "swagger"
};
options.SwaggerEndpoint("/swagger/v1/swagger.json", "waiting_list v1");
services.AddSingleton(options);
Use configured instance:
app.UseSwaggerUI(app.ApplicationServices.GetRequiredService<SwaggerUIOptions>());
Inject instance to any controller/class:
public WeatherForecastController(ILogger<WeatherForecastController> logger, SwaggerUIOptions swaggerOptions)
{
}
Property RoutePrefix contains swagger prefix (without leading '/')
This idea works only if options object passed to UseSwaggerUI method (available since version 6.0.0). If UseSwaggerUI invoked using callback (like a UseSwaggerUI(a => { a.RoutePrefix = string.Empty; })) it won't work.
What is going on gang?
I am looking for a proper and clean way of injecting configuration into my classes using the IOptions package. Currently, I am registering all the configuration object in one place and everything is just fine. The problem is with some classes that I need to register in the startup method.
The initial method implementation looks like this: (extension method)
public static void RegisterHttpClients(this IServiceCollection services, AppSettings appSettings)
{
services.AddHttpClient<IPdfService, PdfService>(
httpClient => httpClient.DefaultRequestHeaders.Add("PdfApiKey", appSettings.PdfApiKey));
}
This AppSettings object I am also injecting into other classes using IOptions so would like to somehow reuse the object from there instead of having to get same object from json again like that:
var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();
services.RegisterHttpClients(appSettings);
In other words - I already have the thing registered with services.Configure<AppSettings>(configuration.GetSection("AppSettings")); so would kind of like to piggy-back on this if possible.
Any thoughts/suggestions for possible directions on how to configure this?
EDIT: (added JSON and AppSettings files)
public class AppSettings
{
public string PdfApiKey {get; set;}
public string AnotherProperty {get; set;}
}
{
"AppSettings": {
"PdfApiKey": "SomeKey",
"AnotherProperty": "HollyTheCow"
}
}
Thanks in advance!
You can accomplish this by registering your options with Configure (which you've done), then using the second overload to AddHttpClient() to get the registered options from the DI container (Service Locator pattern). Note I've used IServiceProvider.GetRequiredService, but you can use IServiceProvider.GetService()?.Value if it's an optional setting.
services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
services.AddHttpClient<YourService>((serviceProvider, config) => {
var settings = serviceProvider.GetRequiredService<IOptions<AppSettings>>().Value;
config.BaseAddress = new Uri(settings.BaseUri);
});
Your code has no any error and will run successfully
There may be many appSetting.json files on your project.
Check the ASPNETCORE_ENVIRONMENTvariable on your project.
Right click on your project > Properties > Debug > Environment variables
if ASPNETCORE_ENVIRONMENT is set on Development IConfiguration read data from
appSettings.development.json and you should add AppSettigs json in appSettings.development.json
if ASPNETCORE_ENVIRONMENT is set on Production IConfiguration read data from
appSettings.production.json and you should add AppSettigs json in appSettings.production.json
if you want IConfiguration read data from appSetting.json delete both appSettings.development.json and appSettings.production.json file.
in this case IConfiguration by default read and bind data from appSetting.json
Another options
You can config your IConfiguration to load data dynamically from
appSettings.json like this
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.configuration = builder.Build();
}
The problem:
The dotnet ef Migrations add MyNewMigration command fails with:
No database provider has been configured for this DbContext. A
provider can be configured by overriding the DbContext.OnConfiguring
method or by using AddDbContext on the application service provider.
If AddDbContext is used, then also ensure that your DbContext type
accepts a DbContextOptions object in its constructor and
passes it to the base constructor for DbContext.
There are many SO posts regarding this issue, and I have read most of them. The one that seems to have the exact same problem is here:
EF Core unable to run commands. Error: No database provider has been configured for this DbContext
However, the issue was never resolved. Here are some bullets that sums the investigation up and further down, details about the steps.
We had initially just hardcoded the connection string in the DbContext OnConfiguring method, and then the Migrations command worked well.
We then proceeded to use a static method, that read the appsettings.json file. This worked when running the app, but it did not work when running the Migrations add command, because the connectionString we fetched from the static class, always returned null.
We then moved to use dependency injection, like everyone everywhere suggests doing. in the Startup.cs, we use something like services.AddDbContext<MyDbContext> and in the MyDbContext we have a constructor like public MyDbContext(DbContextOptions<MyDbContext> options) : base(options). The constructor is called, the connectionstring is there, app can run, but Migrations Add fails with same error message as above.
I then tested removing the default empty constructor, just keeping the MyDbContext(DbContextOptions<MyDbContext> options) constructor. The error message from the command was then "Unable to create an object of type 'MyDbContext'."
Since I run the commands from my Data project (where entities, dbcontext etc exists), I tried adding a startup path to the command, like dotnet ef Migrations add MyMigrationStuff --startup-project C:\Git\MyProject\MyProject.Api, still without default/empty constructor. This time, the error message was simply Build failed. I then reinstated the empty construtor, ran same command again: then I get the same error as above.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ... other stuff
string cString = configuration["ConnectionStrings:MyDb"]; // cString is correct and valid!
services.AddDbContext<MyDbContext>(options => options.UseMySql(cString, mySqlOptions => mySqlOptions
.ServerVersion(new ServerVersion(new Version(5, 0, 17), ServerType.MySql))));
// services.AddDbContext<MyDbContext>(); // without DI as in case 1 and 2 above
}
cString looks correct when running app:
MyDbContext:
public class MyDbContext : DbContext
{
// DbSet etc...
public MyDbContext() : base()
{
}
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
// If I break here, the options has two Extensions, looks ok, see below.
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
}
This is the options in the MyDbConstructor:
appsettings.json
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"ConnectionStrings": {
"MyDb": "Server=localhost;Port=3308;Database=mydb;User=root;Password=root;"
}
}
I am running ASP.NET Core 3.1.101, EF Core 3.1.1, visual studio 2019.
you need to install MySQL provider using this cmd:
dotnet add package MySql.Data.EntityFrameworkCore
then add this code to your DBContext class constructor:
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseMySQL(Configuration.GetConnectionString("MyDb"));
}
finally, add this line into your startup.cs class instead of what you did put:
services.AddDbContext<MyDbContext>();