Pass url parameter from Appsettings.json (.core) to React - c#

I am trying to access the url parameter inside the appsettings.json file. The reason is that the api URL differs from Development to publish. I am not entirely sure the best way to solve this.
I've found a solution that could work:
how to get config data from appsettings.json with asp.net core & react
As I've understood from above thread, I need to create a service and call it from React? This seems abit wierd since I always(?) need to do a API request to the same project to recieve the URL which is needed for the API requests.
The other suggestion is to use webpack, or somehow save the url in the clientside. But won't this mean that whenever I need to change environment, I need to change that in 2 places (backend & frontend)?
This is how my appsettings.json file looks (same variable but different values for .Development and .Publish).
{
"Url": "localhost:44000"
}
In my Startup class I am getting the value:
var urlValue =
_configuration.GetSection("Url");
Can't I somehow get the value once depending on environment from the backend and recieve it using React?
Not sure if I am thinking wrong here?
Would appreciate if anyone is able to point me to the right direction.

My solution to this is using .env documents but still using .core launchSettings env.
For this solution to work, I was in need of env-cmd:
npm install env-cmd
Added 3 .env files:
1. .env
2. .env.development
3. .env.prod
Each file contains a url string:
REACT_APP_Url = https://localhost:44000
I added env check in .core startup file:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
else if (env.IsProduction())
{
spa.UseReactDevelopmentServer(npmScript: "build");
}
});
When I am running production code - I am refering to "build", so in my case the object "build" is going to run.
package.json file:
"scripts": {
"start": "rimraf ./build && react-scripts start",
"build": "cross-env env-cmd -f .env.prod react-scripts start",
}
I can now access the url by using:
{process.env.REACT_APP_Url}

Related

.net core 3.1 call to User Secrets for SendGrid keys returns null values

I have set up a project following https://learn.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm?view=aspnetcore-3.1&tabs=visual-studio
From Startup.cs:
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
_secretOne = Configuration["SecretStuff:SecretOne"];
the _secretOne variable was added to prove that the correct secrets.json file is being accessed. It has both a SecretStuff block and a AuthMessageSenderOptions block.
In EmailSender.cs
public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor)
{
Options = optionsAccessor.Value;
}
A breakpoint after Options = shows the keys with null values.
Eventually I gave up and hard coded the Options.SendGridKey and Options.SendGridUser and with this change the project works as it should.
This is my first use of User-Secrets so when it did not work I set up a console app that references the same secrets.json file and it sends emails.
I found a possible answer in: https://www.twilio.com/blog/2018/05/user-secrets-in-a-net-core-web-app.html
I changed Startup.cs to:
services.Configure<AuthMessageSenderOptions>(Configuration.GetSection("AuthMessageSenderOptions"));
and now the values are as they should be. I sure wasted a lot of time looking in the wrong places for an answer.

Dot Net Core 3.1 - Unable to implement Mongo Atlas healthcheck

I am unable to add the Mongo Atlas health check in dot net core 3.1 using the AspNetCore.HealthChecks.MongoDb nuget package. Added below code into the startup.cs
services.AddHealthChecks().AddMongoDb("MongoDbContext");
endpoints.MapHealthChecks("/api/v1.0/health", new HealthCheckOptions()
When I hit the health URL, it is giving an exception as below
"Status": "Unhealthy",
"Description": null,
"Exception": "MongoDB.Driver.MongoCommandException: Command listCollections failed: not authorized on test to execute command
when you have all the configuration stuff done, you should try with adding first the health check definition for MongoDB:
public void ConfigureServices (IServiceCollection services) {
services.AddControllers ();
string mongoDBConnection = Configuration.GetValue ("mongoDB:connection");
services.AddHealthChecks ()
.AddMongoDb (mongodbConnectionString: mongoDBConnection,
name: "todo-db-check",
failureStatus : HealthStatus.Unhealthy,
tags : new string[] { "todo-api", "mongodb" });
}
Also you need to add connection string info for MongoDB in the app settings file.
"mongoDB:connection": "mongodb://localhost:27017"
Then, add the health check with “/hc” path to the request pipeline.
app.UseEndpoints (endpoints => {
endpoints.MapControllers ();
endpoints.MapHealthChecks ("/hc");
});
Now you can run the API and hit the “/hc” endpoint via browser, and it should be in healthy status with its database.
You can also see the documentation for MongoDB (https://docs.mongodb.com/manual/), and and for that specific issue, you should take a look at these pages, to make it clearer.
(https://rmauro.dev/adding-health-checks-to-net-core-application/)
(https://www.gokhan-gokalp.com/en/aspnet-core-series-06-monitor-the-health-of-your-applications-by-implementing-health-checks-and-azure-application-insights/)
Looking at the source code on github
if (!string.IsNullOrEmpty(_specifiedDatabase))
{
// some users can't list all databases depending on database privileges, with
// this you can list only collections on specified database.
// Related with issue #43
using var cursor = await mongoClient
.GetDatabase(_specifiedDatabase)
.ListCollectionNamesAsync(cancellationToken: cancellationToken);
await cursor.FirstAsync(cancellationToken);
}
the problem seems to be that you don't have enough privileges in Atlas for listing the collections on your database "test"

Store Swagger UI Tokens Permanently after Browser Refresh or Computer Restart

Is there any method to hardcode tokens into Swagger UI permanently in Local computer or Dev Environment ?
We are testing Net Core Apis and also running them through Angular 8: developing, rebuilding, writing code, testing Swagger APIs over 100 times a day each.
Looking for way, to store token below, so don't have to keep Reentering.
Can be very cumbersome, consuming time, as minutes add up.
Maybe we can read a token from external file in developer desktop. Token stays so even after computer restarts, developers are not required to reenter tokens. Perhaps in appsettings.json or any file?
Or anyway to inject code with Net Core Visual Studio Environment, that does not expose token in Source control?
Answer should run all Swagger UI and APIs ran from Angular environment,
Only in QA and Production will require entry of token
Using Angular and Net Core 2.0 C#,
I managed to do it in ASP.NET Core 5 by adding this line to startup.cs, Configure method
app.UseSwaggerUI(c =>
{
c.ConfigObject.AdditionalItems.Add("persistAuthorization","true");
});
I found this by reading this docs
And here
Adapting my other answer to your case, your setup can look like follows:
wwwroot/swashbuckle.html
<!-- your standard HTML here, nothing special -->
<script>
// some boilerplate initialisation
// Begin Swagger UI call region
configObject.onComplete = () => {
// this is the important bit, see documentation
ui.preauthorizeApiKey('api key', 'HARDCODE YOUR KEY HERE' );// key name must match the one you defined in AddSecurityDefinition method in Startup.cs
}
const ui = SwaggerUIBundle(configObject);
window.ui = ui
}
</script>
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
.........
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new Info { Title = "You api title", Version = "v1" });
c.AddSecurityDefinition("api key", new ApiKeyScheme() // key name must match the one you supply to preauthorizeApiKey call in JS
{
Description = "Authorization query string expects API key",
In = "query",
Name = "authorization",
Type = "apiKey"
});
var requirements = new Dictionary<string, IEnumerable<string>> {
{ "api key", new List<string>().AsEnumerable() }
};
c.AddSecurityRequirement(requirements);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
if (env.IsDevelopment()) // override swashbuckle index page only if in development env
{
c.IndexStream = () => File.OpenRead("wwwroot/swashbuckle.html"); // this is the important bit. see documentation https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md
}
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); // very standard Swashbuckle init
});
app.UseMvc();
}
There are different ways to deliver your key to swagger, hard-coding might be not the best, but it should hopefully get you started.
Since you indicate you only want this functionality for development environment I opted to only serve the modified file if (env.IsDevelopment()), which you, again, can tweak to your needs
you add this functionality through swashbuckle
https://cpratt.co/customizing-swagger-ui-in-asp-net-core/
Enable bearer token in Swashbuckle (Swagger document)

EF Core multiple database same schema

Using EF Core .net 2.2.
Trying to have an app where there is a "live" database and a "test" database backing my app. Currently I publish multiple sites each with their own DBContexts and just before publishing I comment out and swap the code for the connection string/db in my startup.cs.
ex:
//services.AddDbContext<DataContext>(options =>
// options.UseSqlServer(Configuration.GetConnectionString("TestDataContext")));
services.AddDbContext<DataContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LiveDataContext")));
Then my two sites are
testdata.site.com and livedata.site.com
This works but it is time consuming and inefficient whenever updates are made to the site/controllers/views etc. Plus if i ever wanted more than two sites to share the same database schema, the publishing work required would compound even more.
Here is my ideal solution but I don't know how to accomplish it:
I want to send route data to the controller and have the controller decide the connection string when it does this portion of the controller:
private readonly POSContext _context;
public CashierController(POSContext context)
{
_context = context;
}
Example, the URL would be something like:
www.site.com/{test or live}/{controller}/{action}
Then a user could swap between the databases on the fly if needed.
I can work through the routing portion but I am really stuck on what to with the controller and startup database portion to make this work.
Anyone have an idea or can get me going on the right path?
It all depends on how you publish your applications and what level of control you have on your hosting server.
You can use multiple configuration files which have different connection string values, so instead of having two connection string names, you should have only one, for example, "MyAppConnectionString", and use environment based configuration files to override it when needed.
To read more about configuration, visit:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2
Alternatively, you can use the hosting environment capability:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-2.2
Please find some useful information in this answer as well:
Automatically set appsettings.json for dev and release environments in asp.net core?
This is what I ended up doing. I looked at what #Norcino said above and referenced the links in his post.
I created multiple Appsettings{DBIdentifier}.json files (still kept the usual appsettings.json file as well), ex appsettingsste3.json and in these JSON files I put a connection string, all with the same DB Name, but pointing to different DBs on my SQL server.
ex:
{
"ConnectionStrings": {
"SiteDBContext":\\Connection string for unique DB, all with same schema/tables, etc\\
}
}
In my program.cs I created a function that looks at the current directory on my web server, since each site is in its own folder on my webserver (ex d:\inetpub\wwwsites\ste1, d:\inetpub\wwwsites\ste3, d:\inetpub\wwwsites\ste3), then take the last four characters of that string then run a switch statement on adding the extra json file.
The portion of program.cs that i modified looks like this:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var dirStr = Directory.GetCurrentDirectory().ToString(); //Gets path of directory into string
var lastFour = dirStr.Substring(dirStr.Length - 4); //gets name of directory (last four characters in path)
switch (lastFour)
{
case "ste1":
case "ste2":
case "ste3":
string appStr = "appsettings" + lastFour.Substring(3) + ".json";
config.AddJsonFile(appStr, optional: false);
break;
}
})
.UseStartup<Startup>()
.Build();
Then of course, ConfigureServices in Startup.cs needs this:
services.AddDbContext<DataContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SiteDBContext")));
Have not yet performed hard testing to know what performance will be like but i think it should be fine since program.cs only runs when the app is first started and so once the app is running there shouldn't be any performance degradation at all. (Am I right?)

Invalid cache key parameter specified when enabling caching for a path parameter in AWS API Gateway

I have a serverless web API (API Gateway + Lambda) that I have built in C# and deployed via Visual Studio. This is achieved via a serverless.yml file that auto-creates a CloudFormation template, then that template is applied to create the API stack.
Once my stack is deployed, I have gone into the AWS Console to enable caching on one of the path parameters, but get this error:
!https://ibb.co/B4wmRRj
I'm aware of this post https://forums.aws.amazon.com/thread.jspa?messageID=711315&#711315 which details a similar but different issue where the user can't uncheck caching. My issue is I can't enable it to begin with. I also don't understand the steps provided to resolve the issue within that post. There is mention of using the AWS CLI, but not what commands to use, or what to do exactly. I have also done some reading on how to enable caching through the serverless.yml template itself, or cloud formation, but the examples I find online don't seem to match up in any way to the structure of my serverless file or resulting CF template. (I can provide examples if required). I just want to be able to enable caching on path parameters. I have been able to enable caching globally on the API stage, but that won't help me unless I can get the caching to be sensitive to different path parameters.
serverless.yml
"GetTableResponse" : {
"Type" : "AWS::Serverless::Function",
"Properties": {
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetTableResponse",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [ "AWSLambdaBasicExecutionRole","AWSLambdaVPCAccessExecutionRole","AmazonSSMFullAccess"],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "kata/table/get/{tableid}",
"Method": "GET"
}
}
}
}
}
},
"Outputs" : {
"ApiURL" : {
"Description" : "API endpoint URL for Prod environment",
"Value" : { "Fn::Sub" : "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" }
}
}
--Update Start--
The reason, you are getting Invalid cache key parameter specified error because you did not explicitly highlighted the path parameters section.
This is because, although the UI somehow extrapolated that there is a
path parameter, it has not been explicitly called out in the API
Gateway configuration.
I tested with below and was able to replicate the behavior on console. To resolve this, follow my Point 1 section full answer.
functions:
katatable:
handler: handler.katatable
events:
- http:
method: get
path: kata/table/get/{tableid}
--Update End--
Here you go. I still don't have your exact serverless.yml so I created a sample of mine similar to yours and tested it.
serverless.yml
functions:
katatable:
handler: handler.katatable
events:
- http:
method: get
path: kata/table/get/{tableid}
request:
parameters:
paths:
tableid: true
resources:
Resources:
ApiGatewayMethodKataTableGetTableidVarGet:
Properties:
Integration:
CacheKeyParameters:
- method.request.path.tableid
Above should make tableid path parameter is cached.
Explanation:
Point 1. You have to make sure in your events after your method and path, below section is created otherwise next resources section of CacheKeyParameters will fail. Note - boolean true means path parameter is required. Once you explicitly highlight path parameter, you should be able to enable caching via console as well without resources section.
request:
parameters:
paths:
tableid: true
Point 2. The resources section tells API Gateway to enable caching on tableid path parameter. This is nothing but serverless interpretation of CloudFormation template syntax. How did I get that I have to use ApiGatewayMethodKataTableGetTableidVarGet to make it working?. Just read below guidelines and tip to get the name.
https://serverless.com/framework/docs/providers/aws/guide/resources/
Tip: If you are unsure how a resource is named, that you want to
reference from your custom resources, you can issue a serverless
package. This will create the CloudFormation template for your service
in the .serverless folder (it is named
cloudformation-template-update-stack.json). Just open the file and
check for the generated resource name.
What does above mean? - First run serverless package without resources section and find .serverless folder in directory and open above mentioned json file. Look for AWS::ApiGateway::Method. you will get exact normalized name(ApiGatewayMethodKataTableGetTableidVarGet) syntax you can use in resources section.
Here are some references I used.
https://medium.com/#dougmoscrop/i-set-up-api-gateway-caching-here-are-some-things-that-surprised-me-7526d954fbe6
https://serverless.com/framework/docs/providers/aws/events/apigateway#request-parameters
PS - If you still need CLI steps to enable it, let me know.

Categories

Resources