IOptions configuration binding not working in dotnet-isolated function [duplicate] - c#

I'm attempting to port an existing Functions app from core3.1 v3 to net5.0 I but can't figure out how to get the IOptions configuration pattern to work.
The configuration in my local.settings.json is present in the configuration data, and I can get to it using GetEnvironmentVariable. Still, the following does not bind the values to the IOptions configuration like it used to.
.Services.AddOptions<GraphApiOptions>()
.Configure<IConfiguration>((settings, configuration) => configuration.GetSection("GraphApi").Bind(settings))
The values are in the local.settings.json just as they were before:
"GraphApi:AuthenticationEndPoint": "https://login.microsoftonline.com/",
"GraphApi:ClientId": "316f9726-0ec9-4ca5-8d04-f39966bebfe1",
"GraphApi:ClientSecret": "VJ7qbWF-ek_Amb_e747nXW-fMOX-~6b8Y6",
"GraphApi:EndPoint": "https://graph.microsoft.com/",
"GraphApi:TenantId": "NuLicense.onmicrosoft.com",
Is this still supported?
What am I missing?

I had the same issue, but turns out that the json was not correctly formatted.
Just for reference, here it is how I configured it:
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s =>
{
s.AddOptions<ApplicationSettings>().Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection(nameof(ApplicationSettings)).Bind(settings);
});
})
.Build();
And here is an example of local.setting.json:
{
"IsEncrypted": false,
"Values": {
"ApplicationSettings:test": "testtest",
"ApplicationSettings:testtest": "test"
}
}

Related

Bind gRPC services to specific port in aspnetcore

Using aspnetcore 3.1 and the Grpc.AspNetCore nuget package, I have managed to get gRPC services running successfully alongside standard asp.net controllers as described in this tutorial.
However I would like to bind the gRPC services to a specific port (e.g. 5001), preferably through configuration instead of code if possible. This is because I would like to limit how my gRPC services are exposed.
The closest I have come has been using RequireHost when mapping the endpoints:
// Startup.cs
public void Configure(IApplicationBuilder app)
{
// ...
app.useEndpoints(endpoints =>
{
endpoints.MapGrpcService<MyService>()
.RequireHost("0.0.0.0:5001");
});
}
This seems to do what I want but I can't find any documentation about it, and it requires configuration in code per service. Perhaps there is a better way?
This works (server side) with Kestrel:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5005, configure => configure.UseHttps());
});
webBuilder.UseStartup<Startup>();
});
client side:
var httpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
using var channel = GrpcChannel.ForAddress("https://localhost:5005", new GrpcChannelOptions { HttpHandler = httpHandler } );
var client = new Greeter.GreeterClient(channel);
Note:
var httpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
when you have a self-signed certificate without a trust chain (mostly when developing).
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
Is for support of http.
You need to configure the middleware
app.UseRouting();
app.MapWhen(context => {
return context.Connection.LocalPort == 1000
}, newApp => {
newApp.UseRouting();
newApp.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<Service1>();
}
});
app.MapWhen(context => {
return context.Connection.LocalPort == 2000
}, newApp => {
newApp.UseRouting();
newApp.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<Service2>();
}
});
In the ASP.NET Core 6.0 ports can be changed in the Properties > launchSettings.json file. But this file is considered only if you run the server from the Visual Studio or VS Code.
I was trying to run the server directly using the .exe file for testing. The server was running with the default ports: "http://localhost:5000;https://localhost:5001".
Finally, I changed it from the appsettings.json for the .exe file:
"AllowedHosts": "*",
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "https://localhost:7005",
"Protocols": "Http1AndHttp2"
},
"gRPC": {
"Url": "http://localhost:5005",
"Protocols": "Http2"
}
}
As far as I know, there is no other way to set a specific port for the GRPC service.
The grpc service is also running on the asp.net core kestrel server, the server will listen the port not the service.
If your asp.net core application just has GRPC service, you could just set the kestrel server's listen port to 5001.
If you have multiple service like MVC web api or else, RequireHost is the best workaround to allow only specific port access the grpc service.
If you want to prompt the routing system for GRPC service to require the specified port, you could use below port:
routes.MapGrpcService<MyService>().RequireHost("*:5001");
You can try to use the UseWhen method to use the MapGrpcService endpoints only when the request uses the port you defined.
var grpcPort = 5001;
app.UseWhen(context => context.Connection.LocalPort == grpcPort,
builder =>
{
builder.UseRouting();
builder.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<MyService>();
});
});
This has the benefit of not repeating .RequireHost("*:5001"); for every single service, although repeating UseRouting twice may induce weird behaviour: for instance, authentication may not be working unless you put in builder.UseAuthentication() after builder.UseRouting().
However, this behaviour be useful if you want to have a distinct request pipeline for REST and gRPC.

How to fix "No event hub receiver named <name>"

I am writing an azure webjob which reads events from eventhub using NET Core 3.1.
I have a config file as below:
{
"JobHostConfig": {
"DashboardConnectionString": "",
"StorageConnectionString": "xx"
},
"EventHubConfig": {
"EventHubConnectionString": "xx",
"EventHubName": "xx",
"EventProcessorHostName": "xx",
"ConsumerGroupName": "xx",
"StorageConnectionString": "xx",
"StorageContainerName": "xx"
}
}
In the Main method, I call ConfigureServices method which looks something like:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory() + $"\\..\\..\\..\\ConfigFiles")
.AddJsonFile($"applicationConfig.json", optional: true, reloadOnChange: true)
.AddJsonFile($"applicationConfig.{environment}.json", optional: true, reloadOnChange: true);
Configuration = builder.AddEnvironmentVariables()
.Build();
services.AddSingleton<IConfigurationRoot>(Configuration);
services.AddSingleton<IConfiguration>(Configuration);
services.AddMvcCore();
services.AddSingleton(GetInstance<EventHubConfig>());
services.AddSingleton(GetInstance<JobHostConfig>());
I confirmed that at runtime configs are getting populated in Configuration only like this: Configuration["EventHubConfig:EventHubName"]. But I also debugged that environment variables have not been set and its value is null.
So when I do:
ProcessEvent([EventHubTrigger("%EventHubName%", ConsumerGroup = "%ConsumerGroupName%", Connection = "%ConnectionString%")] EventData eventData) I get that %EventHubName% is not resolved.
Also, when I hard-code the values of these then I get: No event hub receiver named.
Can someone suggest what is wrong with my registration?
Furthermore, I replaced the values with string in EventHubTrigger, and I get Value cannot be null. Parameter name: receiverConnectionString
When using %% format, you should use Custom binding expressions.
For more details, please refer to this answer.
Please let me know if you still have more issues about it.
Right click the appsettings.json file -> click propertities -> set the "Copy to output directory" to "copy if newer"
and the code should be
public static void Trigger([EventHubTrigger("my eventhub name",Connection = "EventHubConnectionString")] EventData message, ILogger logger)
{
string data = Encoding.UTF8.GetString(message.Body);
Console.WriteLine(data+";;xxx");
}
Make your appsettings.json simpler like
{
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx;EndpointSuffix=core.windows.net",
"EventHubConnectionString": "Endpoint=sb://xxxx"
}

ArgumentNullException for ConnectionString When trying to remotely connect to .net core API

First of all is this happening in a Mac and I'm new to dotnet core.
I have installed dockers and setup everything in dotnet core. I did add connectionstring to the 'appsettings' and 'appsettings(Development)'.
"ConnectionStrings": {
"Default": "server=localhost; database=Monitor; User ID=sa; Password=MyComplexpPassword!234;"
},
This is Program.cs file Main method
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
This is startup.cs class ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<MonitorDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default")));
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddScoped<IUserRepository,UserRepository>();
}
This is a Controller test method to test API.
[HttpGet("getUser")]
public UserResource GetUserInfo()
{
var user_1 = new User();
user_1.FirstName = "MAC";
user_1.LastName = "OS TEST";
user_1.Username = "Apple#gmail.com";
return mapper.Map<User, UserResource>(user_1);
}
This method will perfectly execute If I make a rest call(http) without setting up Program.cs class for remote access.
Now I have set it up to run in 'http://0.0.0.0:6001', So that I can access the API from my phone or from another pc in the same wifi.
I followed This instructions.
Now My Program.cs main method is like this.
public static void Main(string[] args)
{
// CreateWebHostBuilder(args).Build().Run();
var configuration = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
var hostUrl = configuration["hosturl"];
if (string.IsNullOrEmpty(hostUrl))
hostUrl = "http://0.0.0.0:6000";
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls(hostUrl) // <!-- this
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseConfiguration(configuration)
.Build();
host.Run();
}
In terminal I ran this command dotnet run --hosturl http://0.0.0.0:6001
If try to access same method as before this happens.
Notice:- I changed only the host, Because I need to test the API with other devices.
I have other controllers and methods that are connecting to the database and do crud operations with it, Those API calls also face the same issue like this. This only happens if I set it up to remote access.
Notice:- If I change the Startup.cs class Connection string line like this, It will work flawlessly in both configurations.
services.AddDbContext<MonitorDbContext>(options => options.UseSqlServer("server=localhost; database=Monitor; User ID=sa; Password=MyComplexpPassword!234;"));
But I felt that this is not good practice. In future, I have to add JWT Authentication to the API so that APP_Secret also needed to add to the AppSettings.json file.
Thank you.
you didn't tell the application to use appsettings.json. change below configuration
var configuration = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
To
var configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddCommandLine(args)
.Build();
As an alternative, you can use the static WebHost.CreateDefaultBuilder method which by default loads settings from 'appsettings.json', 'appsettings.[EnvironmentName].json', and command line args.
Note -> As stated here:
AddCommandLine has already been called by CreateDefaultBuilder. If you
need to provide app configuration and still be able to override that
configuration with command-line arguments, call the app's additional
providers in ConfigureAppConfiguration and call AddCommandLine last.
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
// Call other providers here and call AddCommandLine last.
config.AddCommandLine(args);
})
.UseStartup<Startup>();

ASP.NET Core web service does not load appsettings.json into configuration

I have an ASP.NET Core 2.1 Web Application with Razor Pages which has AAD authentication information defined in the appsettings.json file (courtesy of the default application template - see below on how I got there). However, when trying to configure the authentication in Startup.cs the configuration does not have any of the config values from my appsettings.json. If I inspect the IConfiguration object in the debugger then it appears to only have the environment variable configurations:
Here's the Startup.ConfigureServices method where the issue lies:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options =>
{
// This is from the default template. It should work, but the relevant settings aren't there so options isn't populated.
this.Configuration.Bind("AzureAd", options);
// This of course works fine
options.Instance = "MyInstance";
options.Domain = "MyDomain";
options.TenantId = "MyTenantId";
options.ClientId = "MyClientId";
options.CallbackPath = "MyCallbackPath";
});
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
And the service configuration in case it's important (note that this is being built on top of a service fabric stateless service):
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");
return new WebHostBuilder()
.UseKestrel(opt =>
{
int port = serviceContext.CodePackageActivationContext.GetEndpoint("ServiceEndpoint").Port;
opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
{
listenOptions.UseHttps(GetCertificateFromStore());
listenOptions.NoDelay = true;
});
})
.ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseUrls(url)
.Build();
}))
};
}
To create this service, I used the wizard in VS2017. I selected an existing service fabric project (.sfproj) and chose Services > Add > New Service Fabric Service and chose Stateless ASP.NET Core [for .NET Framework], then on the next page I chose Web Application (the one with Razor Pages, not MVC) and clicked Change Authentication where I chose Work or School Accounts and entered my AAD info. The only changes I have made to this template were adding the code inside the call to AddAzureAD in Startup.ConfigureServices and setting the appsettings.json files to always be copied to the output directory.
Why doesn't the appsettings.json file get loaded into the configuration? As I understand, this is supposed to happen by default, but something seems to be missing...
WebHostBuilder doesn't load appsettings.json by default, you need to manually call AddJsonFile. For example:
return new WebHostBuilder()
.UseKestrel(opt =>
{
//snip
})
.ConfigureAppConfiguration((builderContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false);
})
.ConfigureServices(
services => services
.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseUrls(url)
.Build();
Alternatively you can use WebHost.CreateDefaultBuilder which will load more defaults.
Another approach, would be to manually create the configuration via ConfigurationBuilder then use the UseConfiguration method.
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.Build();
var host = new WebHostBuilder()
.UseConfiguration(configuration)
.UseKestrel()
.UseStartup<Startup>();
The primary intent is core to provide a bit of flexibility when implementing, they often error on less is more. You have to explicitly say what you would like, that way the pipeline remains relatively small.
For others like me who find this issue:
It might be that you're not copying the appsettings.json file during build.
The OP does say he's doing this, but it's kind of a small print thing - and was what I was failing to do.
The more you know ...
Below mentioned steps worked for me
Go to Appsettings.json
Right click and goto properties
select the build action from the drop down to none if its content
Make the copy to Output directory as Copy Always
As mentioned before WebHostBuilder do not execute this default behavior CreateDefaultBuilder needs to be called instead.
I prefer following implementation:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
try
{
logger.LogInformation("Starting up");
host.Run();
}
catch (Exception ex)
{
logger.LogCritical(ex, "Application start-up failed");
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Make sure your file name is all lower case.
My mistake was to name the file appSettings.json instead of appsettings.json. When running within a Linux container, the camel-cased file was not loaded.

Kestrel configuration to use a specific port + url

I am using Asp.Net core 2.0.2 on Win 7 with VS2017 (15.3.5).
My current Kestrel configuration looks like this:
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((hostContext, config) =>
{
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
config.Sources.Clear();
config.AddJsonFile("appsettings.json", optional : false);
config.AddJsonFile($"appsettings.{envName}.json", optional : false);
config.AddEnvironmentVariables();
})
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5859);
})
.UseContentRoot(pathToContentRoot)
.Build();
This obviously listens on http://localhost:5859. I want to configure Kestrel so that it ONLY listens on a custom URL such as http://localhost:5859/MyNewApp. How do I do that?
(With Core 1.0, I had used UseUrls("http://localhost:5859/MyNewApp") which partially did the job. It would listen on http://localhost:5859 as well as http://localhost:5859/MyNewApp. Doing the same in Core 2.0.2 results in an exception:
System.InvalidOperationException: A path base can only be configured using IApplicationBuilder.UsePathBase())
With 2.0 you need to leverage UsePathBase as UseUrls was removed from Kestrel. You'll want to do this in your Configure method at startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UsePathBase("/MyNewApp");
}

Categories

Resources