WebApi project deployed at Azure on Linux OS wont find routes - c#

I was following this tutorial about "How to deploy a WebApi on Azure with Linux", the tutorial is targeted to .NET Core 3.1 but in my case its the 2.1. Everything went right except, it won't find any routes. I have a test route and I get notfound.
"//mywebapi.azurewebsites.com/api/Test/Test" doesn't get anything.
Inside of the Program Class, I enabled the Error options to show anything wrong with but still.
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(true) // the default
.UseSetting("detailedErrors", "true")
.UseStartup<Startup>();
}
Update!
I was able to read the logs and I get this :
2020-03-12 05:23:43.022 INFO - Logging is not enabled for this container.
Please use https://aka.ms/linux-diagnostics to enable logging to see container logs here.
2020-03-12 05:23:44.322 INFO - Initiating warmup request to container com_0_3b90f319 for site com
2020-03-12 05:23:46.590 ERROR - Container com_0_3b90f319 for site com has exited, failing site start
2020-03-12 05:23:46.593 ERROR - Container com_0_3b90f319 didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging.
2020-03-12 05:23:46.608 INFO - Stoping site com because it failed during startup.
and

When you use publish profile to deploy webapi, click App Service Linux and then import your profile.
Remember set WEBSITE_WEBDEPLOY_USE_SCM as false in App service Configuration.
BTW, the easiest way is in visual studio, right click your project and choose Publish. Click App Service Linux and create New, set up your app service configuration and it will work well.

Related

What's the correct way to set Blazor Webassembly environment?

My goal is to be able to change the environment name from configuration file. If the server is configured for X, then the client should also use X environment.
You know, in ASP.NET we can use launchSettings.json to configure IIS server to Development, and the "real" server which is published to Production. The WASM client should see the same configuration. I would like to configure the environment only here to reduce the resik of forgetting something when publishing the server.
There are multiple articles describing Blazor environments, but I'm confused setting it up correctly.
To get the environment saw by the client, I added this line to the Client/Program.cs file:
Console.WriteLine("Using environment " + builder.HostEnvironment.Environment);
Experiemets:
If I change the Server/launchSettings.json/profiles/IIS Express/environmentVariables/ASPNETCORE_ENVIRONMENT from Development to Production, the website simply won't load (returns 404).
In some other cases, ASPNETCORE_ENVIRONMENT is just ignored / not accessible by the client.
When running an app locally, the environment defaults to Development. When the app is published, the environment defaults to Production.
That's true sometimes for me, but not always. Some cases the environment is Production even if I run locally.
Blazor.start({ environment: "Staging" }); described in this article has no effect. Also that takes a string, how could I pass the server side config variable's value?
Using web.config, I can always overwrite the environment name. There's two problems:
Why maintain another configuration file? There's already launchSettings.json, and the server will depend on that file. As far as I know, I can't reference other settings from the web.config file.
It's really not the blazor-environment header which controls the environment name. Why?
Remove web.config
In Server/Startup.cs, add:
app.Use(async (context, next) =>
{
context.Response.Headers.Add("blazor-environment", env.EnvironmentName);
await next.Invoke();
});
In Chrome DevTools, I can see the value of the header is indeed Development. But the client prints Production.
If the header is ignored, why would I set it in web.config? It it's set, but the clinet WASM project doesn't read the header, how else it knowns the name? Is the referenced Blazor WASM JavaScript file changed, the environment name is compiled into that?
There is also Client/launchSettings.json generated, which seem to be completely ignored.
There are at least 10 ways described to get the environment name. Some use appsettings.json (which is separately download, or IWebAssemblyHostEnvironment, or IConfiguration, or Razor component ect. All I want is to read the global environment name in C# code parts.
I guess this chaos comes from the fact Blazor was changed in the recent years. I'm a bit lost. Which one should I use? Which should always work? Can I get rid of the need to change both web.config and launchSettings.json?
If your wasm is hosted by an asp.net core app, the call to
app.UseBlazorFrameworkFiles();
should add the headers required automatically. You can check this by looking at the headers passed back in a network monitor for the blazor.boot.json file, which is the request that blazor uses to read the blazor-environment header from.
Note that in the sample where you manually added that header, if you didn't add it before the 'AddStaticFiles', that header probably would have been added to your index page, but NOT the blazor.boot.json file, so it would have had no effect.
One other thing to take note of is that the call to app.UseBlazorFrameworkFiles() MUST go before app.UseStaticFiles(). That one tripped me up, hopefully that helps anyone who comes in here searching for an answer :)
ref:
code where blazor wasm reads header: https://github.com/dotnet/aspnetcore/blob/5fed81205944c528cbf7bb3a4ce369ca37bd3676/src/Components/Web.JS/src/Platform/BootConfig.ts#L29
I'm using Blazor Server and not Blazor Web Assembly, but I'm thinking you can do what I do and just set the environment in the ASPNETCORE_ENVIRONMENT variable. When debugging in Visual Studio, set the variable in the launchSettings.json file (note if you have multiple profiles such as IIS Express and YourProject, the variable should exist for each profile (best to keep the value for the variable the same)). When running the app on a server, the ASPNETCORE_ENVIRONMENT variable should exist as an environment variable on the server (VM or physical on-prem etc) or for Azure Web Site (aka Azure App Service) as a variable under Settings->Configuration. Note for environment variables you typically have to log out the runtime user and restart the service before the new value will be read.
If you have code such as env.IsDevelopment(), env.IsStaging(), or env.IsProduction, you will need to use the standard environment names (Development, Staging, and Production).
For Blazor Server, inside Program.cs CreateHostBuilder method where you call Host.CreateDefaultBuilder(args).CongigureWebHostDefaults(webHostBuilder =>
you can read the environment variable this way:
ConfigurationBuilder = new ConfigurationBuilder()
.AddEnvironmentVariables("ASPNETCORE_");
const string aspnetCoreEnv = "ASPNETCORE_ENVIRONMENT";
var environmentName = Environment.GetEnvironmentVariable(aspnetCoreEnv);
Now add the appropriate appsettings file containing the environment name:
ConfigurationBuilder.AddJsonFile($"appsettings.{EnvironmentName}.json", true, true);
var configuration = ConfigurationBuilder.Build();
webHostBuilder.UseConfiguration(configuration);
var hostBuilderEnvironmentSetting = webHostBuilder.GetSetting(WebHostDefaults.EnvironmentKey);
if (string.IsNullOrEmpty(hostBuilderEnvironmentSetting))
webHostBuilder.UseSetting(WebHostDefaults.EnvironmentKey, EnvironmentName);
If you are using Startup.cs, then you can use constructor injection so you can access the environment within Startup:
public Startup(IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
If Blazor Web Assembly configures differently, maybe you can still achieve the equivalent logic to make it load the appsettings that match the environment.
With this approach, I have not encountered issues where the app does not identify the environment properly. I don't have a web.config file so haven't needed to maintain values there. The only "won't load" issue I have hit was in Azure where my publish config specified a "Microsoft Identity Platform" which references an Azure App Registration and that app registration didn't have under Redirect URIs a Web Platform with URI for my environment-specific app service.

ASP.NET Core 2.2 App Service hosted on Azure returning 500 without an exception being thrown

I have developed an App Service backend hosted on Azure using ASP.NET Core 2.2.
From time to time I'm getting a 500 status but I'm not able to find out where is that 500 being generated.
I'm using application Insights and I see on Failed Requests that sometimes there are some calls to my App Service which are returning 500.
If I reproduce that same call with same values with Postman I'm not getting any 500. There are also some End Client Apps making requests to this backend. I believe it's happening to them but why I can't reproduce the same result if I'm performing the same request with same values.
I know that 500 implies "Internal Server Error" and that it is related to the Application code but I've even configured my App Service to use a Global Error handling but I never get that 500 when I'm executing the same request that I see on Application Insights.
I have set on my Configure on StartUpp class the global error handling (just to show you that I have also enabled that)
app.UseExceptionHandler(config => ExceptionHandlingSettings.ConfigureGlobalExcpetionHandling(config));
I would like to know where or how to get more information in order to find out the code generating the error/exception because I guess there is some bug in my code which throws an unhandled exception.
Here are couple of steps which you can do to understand the root cause:
1- Change stdoutLogEnabled="false" to true and then check the logs at stdoutLogFile=".\logs\stdout". The error(s) there might tell you something.
The ASP.NET Core Module stdout log often records useful error messages not found in the Application Event Log. To enable and view stdout logs:
Navigate to the Diagnose and solve problems blade in the Azure portal.
Under SELECT PROBLEM CATEGORY, select the Web App Down button.
Under Suggested Solutions > Enable Stdout Log Redirection, select the button to Open Kudu Console to edit Web.Config.
In the Kudu Diagnostic Console, open the folders to the path site > wwwroot. Scroll down to reveal the web.config file at the bottom of the list.
Click the pencil icon next to the web.config file.
Set stdoutLogEnabled to true and change the stdoutLogFile path to: \?\%home%\LogFiles\stdout.
Select Save to save the updated web.config file.
Make a request to the app.
Return to the Azure portal. Select the Advanced Tools blade in the DEVELOPMENT TOOLS area. Select the Go→ button. The Kudu console opens in a new browser tab or window.
Using the navigation bar at the top of the page, open Debug console and select CMD.
Select the LogFiles folder.
Inspect the Modified column and select the pencil icon to edit the stdout log with the latest modification date.
When the log file opens, the error is displayed.
Please make sure to remove the logging once the inspection is completed.
2 - Use Middleware for handling erroneous request like app.UseDeveloperExceptionPage();
3- You can add application Insight in your app and try to put Info logging to understand what part of your code is causing the failure.
For additional reference, please check below doc:
https://learn.microsoft.com/en-us/aspnet/core/test/troubleshoot-azure-iis?view=aspnetcore-3.0
https://learn.microsoft.com/en-us/azure/azure-monitor/app/asp-net-exceptions
Hope it helps.

asp.net core 2.0 deployment - InvalidOperationException: The antiforgery token could not be decrypted

Recently I developed a asp.net core 2.0 web app in my company and in debug mode works perfect, however when I deployed in our testing server into IIS and we try to execute from a client machine it ran into a problem:
An unhandled exception occurred while processing the request.
CryptographicException: The key {0851ad3b-df33-4cf7-8c3a-5c637adaa713} was not found in the key ring.
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, bool allowOperationsOnRevokedKeys, out UnprotectStatus status)
InvalidOperationException: The antiforgery token could not be decrypted.
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenSerializer.Deserialize(string serializedToken)
The problem starts when I submmit login page. I investigated links with same problems here and other blogs, but I found that has to be with ValidateAntiForgeryToken and solution is related with Microsoft.AspNetCore.DataProtection. I added nuget package Microsoft.AspNetCore.DataProtection.Redis to my project and I added in ConfigureServices of startup class following code:
var redis = ConnectionMultiplexer.Connect("192.168.10.151:80");
services.AddDataProtection().PersistKeysToRedis(redis, "DataProtection-Keys");
services.AddOptions();
Our testing server ip is 192.168.10.151, however app throws following exception:
RedisConnectionException: It was not possible to connect to the redis server(s); to create a disconnected multiplexer, disable AbortOnConnectFail. InternalFailure on PING
¿Why it doesn't connect since is resolving in the same web app server?
¿Where is DataProtection-Keys database located?
as a workaround, I changed method by using PersistKeysToFileSystem as follows:
services.AddDataProtection()
.SetApplicationName("myapp-portal")
.PersistKeysToFileSystem(new System.IO.DirectoryInfo (#"c:\ProgramData\dpkeys"));
However running app in test server 192.168.10.151, when login form is submitted, goes back to login page. Checking stdout log file, only shows:
Hosting environment: Production
Content root path: C:\inetpub\wwwroot\OmniPays
Now listening on: http://localhost:30064
Application started. Press Ctrl+C to shut down.
Checking network messages by chrome's developers tools I noticed something:
Request URL: http://192.168.10.151/OmniPays/Account/Login
Request Method: POST
Status Code: 302 Found
Remote Address: 192.168.10.151:80
Referrer Policy: no-referrer-when-downgrade
and then ...
Request URL: http://192.168.10.151/OmniPays/Home/Main
Request Method: GET
Status Code: 302 Found
Remote Address: 192.168.10.151:80
Referrer Policy: no-referrer-when-downgrade
AccountController's Login action redirect request to HomeController's Main action only if authentication succeded, and Main action has [Authorize] attribute. For some reasons I can't achieve understand, Main action fails and return to Login page. URL in chrome shows: http://192.168.10.151/OmniPays/Account/Login?ReturnUrl=%2FOmniPays%2FHome%2FMain
I'm using Microsoft Identity. In debug mode works fine and if I deploy app in my local PC on IIS also works fine. ¿Maybe any SDK is missing in the server?
Please need help!!
Solution was found! the cause of problem was not in IIS neither the Server, connection to the server is using http rather than https, no certifies involved to validate secure connection, however testing in differents servers app works ok, so I felt really disappointed. Solution was to remove cookies an any data related with this URL pointing to Development Server (failing) in all browsers, data that was previously stored, and voila!!, now app works perfect. By default, as bhmahler comments data protection is made in memory and I left configuration by default, I mean, not explicitly persistence in redis nor PersistKeysToFileSystem and works fine, however is important to set DataProtection to strong data sensitive protection.
I'm newbie about these topics and It's unbelievable such a simple thing caused on me that waste of time. Thanks to all!.

Application Insights from a non web app returns 404

I' writing an Azure webJob which is ultimately a console app before it is deployed. I'm trying to add Application Insights to this so I can have Telemetry Results. However since this is not a Web app of any sort Telemetry isnt a recognised part of the project.
When I atempt to add telemetry manually and set any details of the TelemetryConfiguration.Active frame work I am presented with the following error:
TelemetryConfiguration.Active.InstrumentationKey = settingsKey;
Exception:
'Microsoft.WindowsAzure.Storage.StorageException' in Microsoft.WindowsAzure.Storage.dll
Additional information: The remote server returned an error: (404) Not Found.
However if I run an ODATA mvc app in the solution all telemetry works fine 'out of the box'.
Any direction of how to set up Insights via the SDK would be very usful as I'm struggling to find and good documentaion for non web app examples.
The host code its self is based on the standard examples of web jobs:
JobHost host = new JobHost();
host.Start();
var task = host.CallAsync(typeof(Functions).GetMethod("MessageHandlingLoop"));
task.Wait();
host.Stop();
Telemetry Code then in the MessageHandlingLoop()
Thanks in advance!
Stack trace that you mentioned is not from ApplicationInsights. SDK does not use WindowsAzure.Storage.
As far as examples. Here is the documentation: https://azure.microsoft.com/en-gb/documentation/articles/app-insights-windows-desktop/
Also you can take a look at how worker role is instrumented (it is closer to console than web examples): https://github.com/Microsoft/ApplicationInsights-Home/tree/master/Samples/AzureEmailService

ASP.NET web application in Azure - How to log errors?

I have a web application deployed to azure but I don't know how to log errors.
For testing purposes I have this ForceError method:
public string ForceError()
{
throw new Exception("just a test exception");
return "ok";
}
Which causes and error like this:
On Azure I enabled all Diagnostic logs like this:
But the error I forced does not appear in selected storage container.
Do you know what should I do to start logging all the errors from the application?
I am afraid just throwing an exception doesn't work in Azure Web application logging.
ASP.NET applications can use the System.Diagnostics.Trace class to log information to the application diagnostics log. The four methods in example below correspond with the diagnostic log levels:
Trace.TraceError("Message"); // Write an error message
Trace.TraceWarning("Message"); // Write a warning message
Trace.TraceInformation("Message"); // Write an information message
Trace.WriteLine("Message"); // Write a verbose message
Besides the basic information for logged events, blob storage log additional information such as the instance ID, thread ID, and a more granular timestamp (tick format) in CSV.
A great article here about logging tips and tools.
See also the Reference to the official Azure Web Apps Logging Document.
On Azure Websites, best way to log would be Application Insights, you can use free version to get insights about crashes/speed/performance.
However, Application Insights is little slower if you enable everything. But if you customize it and enable only error logging, it would push all logs to your azure application insights account and you will be able to monitor/analyze it very nicely.
For more details:
https://azure.microsoft.com/en-in/documentation/articles/app-insights-api-custom-events-metrics/
Instead of automatically configuring Application Insights, I would suggest, take an empty project, setup application insights. Notice all added config files and nuget packages. There is some insight config file, except application key/signature, you can turn off everything.
Only when you want to track an exception manually, you can create TelemetryClient and call TrackException method. You can pass more details if you need.

Categories