How to republish an ASP.NET Core 5 web application in IIS. Changes not taking effect - asp.net-core

I've published an ASP.NET Core 5 Web API to IIS (using VS 2019 webdeploy), and it is running fine. Now I've made some minor changes and republished.
The changes take affect on my local machine using IIS Express, but I do not see them when I publish to my IIS server. I can see that the exe and dlls are updated, but the webapi behaviour does change.
Something as simple as setting the swaggerdoc title doesn't take affect.
Is there something else I need to do to refresh the app? I've tried recycling the app pool and doing an iisreset, but no luck. I've read that IIS is merely acting as a reverse proxy to Kestrel which is actually hosting the api. If so how do I recycle that?
As a test, I delete all the files in my web api directory. I browse to the web api and get the following error:
HTTP Error 500.31 - Failed to load ASP.NET Core runtime Common
solutions to this issue: The specified version of
Microsoft.NetCore.App or Microsoft.AspNetCore.App was not found.
Troubleshooting steps:
Check the system event log for error messages
Enable logging the application process' stdout messages Attach a
debugger to the application process and inspect For more information
visit: https://go.microsoft.com/fwlink/?LinkID=2028526
I then re-publish the web api and browse to it, but it still has the old behavior. I'm really confused, because I know the published dll for my service is correct.
If I use ILSpy I can see the following code:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection appSettingsSection = Configuration.GetSection("ApplicationSettings");
ApplicationSettings appSettings = appSettingsSection.Get<ApplicationSettings>();
services.AddSingleton(appSettingsSection.Get<ApplicationSettings>());
services.AddControllers()
.AddXmlSerializerFormatters()
.AddJsonOptions(delegate(JsonOptions o)
{
o.JsonSerializerOptions.PropertyNamingPolicy = null;
o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
services.AddSwaggerGen(delegate(SwaggerGenOptions c)
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My Demo Service (" + appSettings.Environment + ")",
Version = "v1"
});
});
}
When I browse to the swagger page of my API on my local dev box the title matches code, and the property naming is what I want. When I browse to it on the server, the title of the swagger page is the old value, and the property naming policy is camel case, so I'm pretty sure I'm publishing the new version of my web api, but it's not being executed when I make http requests to the api. Something must be cached somewhere.

Related

Suspected bug in Microsoft Identity Platform with ASP.NET Core 3.1 Razor Pages

I am developing an application to be hosted in the Azure App Services environment which consists of a front-end Web App, a back-end Web API and a SQL Database (using Azure SQL). The front-end Web App is a Razor Pages app. We are trying to use the Microsoft Identity Platform (via Microsoft.Identity.Web and Microsoft.Identity.Web.UI libraries) to acquire an access token for the API when needed.
It works perfectly well the first time, but once a token has been acquired and cached - if the application is restarted it fails with this error:
IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
No account or login hint was passed to the AcquireTokenSilent call.
Startup configuration is (I've tried various variants of this):
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.HandleSameSiteCookieCompatibility();
});
services.AddOptions();
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["Api:Scopes"] })
.AddInMemoryTokenCaches();
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
services.AddRazorPages().AddRazorRuntimeCompilation().AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddMvc();
//Other stuff...
}
I have tried for many days trying to find either a resolution workaround for this. I can catch the
error, but there is no action we can take programmatically that seems to clear the problem (the ITokenAcquisition interface does not offer the option to force an interactive login).
I have found that it is ONLY a problem in a Razor Pages application - a controller-based MVC Web App with almost identical startup code does not exhibit the problem.
I have also found that, by creating a controller-based test MVC Web App and configuring it with the same client id, tenant id etc. as the app we're having problems with, then starting it up (within the Visual Studio development environment) as soon as the main app gets the problem, I can clear the error condition reliably every time. However this is obviously not a viable long-term solution.
I have searched for this problem on every major technical forum and seen a number of similar sorts of issues raised, but none provides a solution to this precise problem.
To replicate:
Create an ASP.NET Core 3.1 Web API.
Create an ASP.NET Core 3.1 Razor Pages Web App that calls the API.
Register both with Azure Active Directory and configure the App to request a token to access the API (as per various MS documents).
Run - if everything is set up correctly the login screen will appear and all will work correctly.
Stop the Web App, wait a couple of minutes and re-start. The error above will now appear.
I have raised a Microsoft support request for it - has anybody else come across this and found a solution for it?
I have finally got to the bottom of this, largely thanks to this: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/216#issuecomment-560150172
To summarise - for anyone else having this issue:
On the first invocation of the web app you are not signed in, and so get redirected to the Microsoft Identity Platform login, which logs you in and issues an access token.
The access token is stored in the In-Memory token cache through the callback.
All then works as expected because the token is in the cache.
When you stop, and then re-start the web app within a reasonably short time, it uses the authentication cookies to pick up the still-current login, and so it does not access the Identity Platform and you do NOT get an access token.
When you ask for a token the cache is empty - so it throws the MsalUiRequiredException.
What isn't really made clear in any of the documentation is that this is supposed to happen - and that exception is picked up by the "AuthorizeForScopes" attribute but only if you allow the exception to fall all the way through and don't try to handle it.
The other issue is that in a Razor Pages app the normal AuthorizeForScopes attribute has to go above the model class definition for every page - and if you miss one it may trigger the above problem.
The solution proposed by "jasonshave" in the linked article solves that problem by replacing the attribute with a filter - so it will apply to all pages.
Maybe I'm a bit old-school, but the idea of using an unhandled exception as part of a planned program control flow doesn't sit right with me - at the very least it should be made clear that that's the intention. Anyway - problem now solved.

UrlHelper returning http links on Azure App Service

I have a service that when deployed on Azure App Services returns http links instead of https links when using UrlHelper. When testing on my development machine it returns https links as expected, and the service is available and accessed through https requests.
An example of the type of route from my startup I'm trying to use is:
routes.MapRoute(
"FooBar",
"api/Foo/{Id}/Bar");
The link is then constructed using:
IUrlHelper _urlHelper = // Injected into class via service registration
int id = 42; // Arbitrary value for example
_urlHelper.Link("FooBar", new {Id = id});
When running on my local machine using Docker on Windows from Visual Studio I get a link of https://localhost:1234/api/Foo/42/Bar, but on my deployed Linux Container App Service on Azure I get http://my-app-name.azurewebsites.net/api/Foo/42/Bar.
I don't know what I'm doing wrong to get an http link instead of an https link, and would appreciate any advice/pointing in the right direction.
So I found the solution was with the configuration of the ASP.Net Core app itself. I performed the following modifications and then everything worked correctly:
Added app.UseForwardedHeaders(); to the request pipeline.
Added the following snippet to service container registration:
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
The KnownNetworks and KnownProxies need to be cleared as they default to assuming an IIS hosting environment. For extra security you can add the known proxy/network IPs instead of clearing them here.

Asp.Net Core signalR is not working if publishing inside virtual directory

I have developed an application in asp.net core and used signalR. When i publish it in root directory it works fine, but if i publish it inside virtual directory it doesn't work. My signalR hub is always pointing to the root directory.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ... other middleware ...
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chat");
});
}
And i have initialized it in client side like this,
chat.js
var connection = new signalR.HubConnection("/chat");
After publishing it inside virtual directory(/development/chatapp/source) it points like this,
http://localhost:100/chat?userId=1
But actually it has to point like this, so that it will work
http://localhost:100/development/chatapp/source/chat?userId=1
I have used asp.net core 2.0 and this signalR version(1.0.0-preview1-final).
Someone please suggest me to resolve this issue.
With the help of my friend i have found the root cause for this issue and i fixed it. Actually javascript doesn't know whether the application is hosted in root folder or sub folder(in virtual directory). It always points the root folder.
So when we are initializing it in js like this(new signalR.HubConnection("/chat")), it points the root directory as below,
http://localhost:100/chat?userId=1
Since javascript doesn't aware of IIS hosting, we need to tell the relative path from c#. So i get my application's base path as follows in controller,
ViewData["PathBase"] = Request.PathBase.ToString();
And if it has some value i just prepend it to '/chat', otherwise i just initialize it as '/chat'.
var connection = new signalR.HubConnection("/development/chatapp/source/chat");
This solves my problem :-)
Kailash P : With the help of my friend i have found the root ...
OK, thanks for your sharing.
I'm publish the SignalR Chat sample to IIS but NOT WORKS --X
SignalRChat :
 Tutorial: Get started with ASP.NET Core SignalR
 https://learn.microsoft.com/zh-tw/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio
*With Visual Studio debugging, the sample are works but NOT WORKS publish to IIS.
In page load, the send message button not enabled because there have some errors :
Failed to load resource: the server responded with a status of 500 (Internal Server Error)  chatHub/negotiate:1
[2019-11-18T06:40:26.977Z] Error: Failed to complete negotiation with the server: Error: Internal Server Error  Utils.ts:179
--
After add the vitural directory the SignalR Chat sample WORKS :
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
->
var connection = new signalR.HubConnectionBuilder().withUrl("/chat1/chatHub").build();
Don't know why there have no any official note about this problem (SignalR publishing).
[NET Core 2.2]
[Visual Studio 2019]

Web Api documentation with swashbuckle

We are currently trying to start writing WebApi services for our products switching from traditional WCF SOAP based services. The challenge we have got is how to provide the api documentation. I came across the SwaggerUi/swash buckle.
One limitation we have is we do not want to host the WebApi services in IIS but in a Windows Service. I am new to Web Api so I might be doing things the wrong way.
So for testing, I am hosting the web api in a console application. I can use the HttpClient to invoke the Get method on the Web Api but I can't access the same if I type the url in a web browser (is this normal for self hosted web api?).
So I installed the Swashbuckle.core nuget package and included the following code in the Startup class (Owin selfhosted).
var config = new HttpConfiguration();
config
.EnableSwagger(c =>
{
c.IncludeXmlComments(GetXmlCommentsPath());
c.SingleApiVersion("v1", "WebApi");
c.ResolveConflictingActions(x => x.First());
})
.EnableSwaggerUi();
private static string GetXmlCommentsPath()
{
var path = $#"{AppDomain.CurrentDomain.BaseDirectory}\WebApiHost.XML";
return path;
}
When I browse to the following location
http://localhost:5000/swagger/ui/index
I get "page cannot be displayed" in IE. Similar for chrome.
Is there anything special that needs to be done when hosting a WebApi in a console/windows service application to get the documentation automatically?
(I have enabled Xml documentation for the project)
I have now attached a test project. Please follow the link below:
Test project
Regards,
Nas
Your problem is not with Swashbuckle, which is configured correctly. Instead it is with the fact that your OWin web app has closed by the time the browser navigates to the swagger URI. Your using statement means that the web app is shut down at the end of it - well before Chrome has opened and navigated to the swagger path. You need to ensure the web app is still running - then your path will be valid (although in your source code you have different ports 9000 and 5000 in your url variables).

ASP.NET WebAPI fails from MVC4 controller

I'm new to ASP.NET Web API and I'm struggling with a very strange problem.
I have some code which calls a RESTful service and it executes fine from a console project, but I can't get it to run from an MVC4 project running under .NET 4.0
The code to call the service is very simple:
internal string Test()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://testserver");
var task = client.GetAsync("/someUri")
var response = task.Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
As mentioned, called from a console project it works as expected and I get a response in milliseconds, however if I call the method from an action in my MVC4 controller after a few seconds I get a message stating that:
"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to repond".
Weirdly, when debugging the MVC4 version, the task status always shows as WaitingForActivation.
Running fiddler doesn't show any request being made from the MVC4 version, but again does for the Console version.
After a fairly serious bit of googling I can't find anyone else who seems to have had this problem, so I'm guessing that I've fundamentally misunderstood something, but at the moment I'm not sure what!
Updated 16:55 BST, 11/09/2012
To make things even weirder, I've just created a new MVC4 site and I can call the method without any problems! I'm now trying to compare the sites, however one was an existing site that was upgraded to MVC4 and the other is a new blank site, so spotting the relevant difference could be tricky.
Updated 16:44 BST, 14/09/2012
This is now looking like some infrastructure / networking issue.
I upgraded the project to VS2012 with .NET 4.5 so that I could use async/await to try the suggested implementations to avoid a deadlock. This didn't change anything so I went back to square 1.
I created a new solution with a new MVC4 project, a new services library and a unit test project to run the service library outside of MVC.
In the service library I created one method to call a public "what's my IP" service, and another to call a company service that's exposed publicly but only responds properly to company IP addresses.
For some background, I connect in to the company LAN via a VPN.
When disconnected from the VPN, in both unit tests and MVC, the IP service responds HTTP 200, the company service responds HTTP 404 as expected.
When connected to the VPN, unit tests both respond HTTP 200, MVC both timeout.
Next I ran MS Soap Tool locally and used that to proxy calls to the company services. All calls (whether from unit tests or MVC) show a request and response, but the unit test registers the response whilst the MVC controller does not.
My only other thought is that it could be something to do with the size of the reply? All the "successes" have very small replies other than the unit test calling the company service?
The Microsoft recommended way to upgrade an MVC3 to MVC4 site is to start with a completely new MVC4 site a migrate your views, controllers & code over. So I think that your upgrade steps may be part of your issue, since you were able to get it to work in the new MVC4 site you created. If you need to manually upgrade your existing site, I would follow the steps outlined in Upgrading ASP.NET MVC 3 Project to ASP.NET MVC 4