I have an ASP.NET Core 6 project deployed as AWS Lambda NET6 runtime and before the upgrade from 3.1 to 6 the code below worked fine returning the remote IP address even when invoked locally.
IHttpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()
Now when I run it locally it resolves to ::1 and I have read that this might be a problem due to localhost which is something I need to overcome since I rely upon the remote IP address.
From articles found in SO (This, This, and This) I have tried the below.
Adding the below middleware in Startup.cs
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto
});
Also added an endpoint to check what is returned locally and the deployed AWS Lambda (works fine).
[HttpGet("ipaddress")]
public IActionResult GetRemoteIpAddress(CancellationToken cancellationToken)
{
return new JsonResult(new
{
requestContextConnection = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
httpContextaccessorContextConnection = _accessor.HttpContext.Connection.RemoteIpAddress.ToString(),
getRequestIp = GetRequestIP()
});
}
Method GetRequestIP() retrieved from this answer. From the below endpoint I get the below results.
Localhost:
{
"requestContextConnection": "::1",
"httpContextaccessorContextConnection": "::1",
"getRequestIp": "::1"
}
AWS Lambda deployed:
{
"requestContextConnection": "SOME.IP.207",
"httpContextaccessorContextConnection": "SOME.IP.207",
"getRequestIp": "SOME.IP.207"
}
How can I fix the localhost issue that is not supported in Kestrel; (from my understanding); I use SDK 6.0.201.
Related
I run .NET core 3.1 app behind in Linux machine with Kestrel behind reverse proxy (Apache).
I constantly push some data to certain endpoint and then relay that data through signalR hub to users:
[HttpPut("{tagName}/live")]
[Authorize(Roles = "Datasource")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[TypeFilter(typeof(TagControllerFilterAttribute))]
public async Task<IActionResult> PutLiveTag(Tag tag, string tagName)
{
bool status = await tagRepository.UpdateLiveData(tagName, tag);
if (status)
{
await tagHubContext.Clients.Group(tagName).SendAsync(tagName, tag);
return Ok();
}
return BadRequest();
}
Users joins and leaves that signalr hub by simply removing them from group:
public class TagDataHub : Hub
{
[HubMethodName("subscribe")]
public async Task JoinTagGroup(string tagName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, tagName);
}
[HubMethodName("unsubscribe")]
public async Task LeaveTagGroup(string tagName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, tagName);
}
}
My app runs in deployment for few hours and then when I try use that app's API endpoints or anything related to that app I get 503 response from originated from Apache. I really don't know why Kestrel stops working...
When I run service dotnet-site status I get that app is still running.
I checked processes in server, I see that dotnet uses lots of resources, but apart from code provided above there is nothing else running in that app:
Please send help I really don't know how to debug/figure out this on linux server, I would value any suggestion.
I also looked this up and did exactly as this person, but it didn't help me: apache mpm worker
I have a .net core api documented with swagger/swashbuckle.
When running the swagger ui on localhost on url https://localhost:44390/ the "Try it out" works fine.
We have the same solution in an App service in Azure with an Azure Front Door acting as reverse proxy. Front Door only accepts https traffic and only forwards https traffic. Front door domain is widget.example.com and App service is widget-test-app.azurewebsites.net. When running the swagger ui in Azure using the url https://widget.example.com/api/index.html there are two differences compared to running in localhost:
The swagger ui is showing a Servers -heading and a dropdown
The swagger ui is showing the server url as https://widget.example.com:80
I added an endpoint in the api with the following code
return $"Host {HttpContext.Request.Host.Host} Port {HttpContext.Request.Host.Port} Https {HttpContext.Request.IsHttps}";
When requesting https://widget.example.com/api/v1/test/url it returns
Host widget-test-app.azurewebsites.net Port Https True
This is completely ok since Front door is changing the host header. Port is empty, though.
Summary: Swagger ui is showing the correct domain in the Servers -dropdown but the port number is wrong. How can I get it to either omit the port number if it's 80 or 443, or add it correctly?
Update: The problem is in the swagger.json file which behind the reverse proxy includes a servers element
"servers": [{
"url": "https://widget.example.com:80"
}]
Startup.ConfigureServices
services.AddApiVersioning(options => {
options.Conventions.Add(new VersionByNamespaceConvention());
});
services.AddVersionedApiExplorer(o => {
o.GroupNameFormat = "'v'VVV";
o.SubstituteApiVersionInUrl = true;
});
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Widget backend v1", Version = "v1"
});
c.SwaggerDoc("v2", new OpenApiInfo {
Title = "Widget backend v2", Version = "v2"
});
c.EnableAnnotations();
c.AddEnumsWithValuesFixFilters();
var xmlFile = $ "{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
Startup.Configure
app.UseSwagger(options => {
options.RouteTemplate = "/api/swagger/{documentname}/swagger.json";
});
app.UseSwaggerUI(options => {
foreach(var description in provider.ApiVersionDescriptions) {
options.SwaggerEndpoint($ "/api/swagger/{description.GroupName}/swagger.json", "widget backend " + description.GroupName);
}
options.RoutePrefix = "api";
});
To fix this I cleared the Servers -list. Here is my code:
app.UseSwagger(options =>
{
options.RouteTemplate = "/api/swagger/{documentname}/swagger.json";
options.PreSerializeFilters.Add((swagger, httpReq) =>
{
//Clear servers -element in swagger.json because it got the wrong port when hosted behind reverse proxy
swagger.Servers.Clear();
});
});
The solution (ok, a - mine - solution :)) is to configure forward headers in Startup.
services.Configure<ForwardHeadersOptions>(options =>
{
options.ForwardHeaders = ForwardHeaders.All; // For, Proto and Host
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Doing this, any URL generation in the app (behind reverse proxy) should respect the port-forwarding value. According to documentation known networks should be specified (taken from docs):
Only allow trusted proxies and networks to forward headers. Otherwise, IP spoofing attacks are possible.
See ASP.NET documentation for more details.
Summary:
I have a .NET Core project that uses the React web app template for the front end. This app uses Entity Framework Core to connect to an Azure SQL Database. I used the Db-Scaffold command to generate my models (just one table at the moment), and created a controller to return this table. Locally, this works fine and the table (JSON) is returned at localhost/api/Users. However when I deploy the website to Azure (CD pipeline is VS 2017 - > GitHub -> DockerHub -> Azure Web App), navigating to mysite.azurewebsites.net/api/Users just renders the login page (React) of my app.
Attempts:
I have tried:
Adding a connection string as a shared value in Azure (named DefaultConnection)
Adding all the outbound IP's of the Azure Web App to the Azure SQL Whitelist
Running the following in the consoles of the web app
fetch('api/users')
This just returns:
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
I have also tried changing database values and refreshing the local version to make sure it was not just a cached page and sure enough the changes were reflected locally.
I also set ASPNETCORE_ENVIRONMENT in the Web App settings in Azure to Production. Although when I go to the error message, page (through the console) I get this:
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<p>
<strong>Request ID:</strong> <code>0HLK3RLI8HD9Q:00000001</code>
</p>
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
Code
UsersController.cs
[Route("api/[controller]")]
public class UsersController : Controller
{
private readonly AccrubalanceDbContext _context;
public UsersController(AccrubalanceDbContext context)
{
_context = context;
}
// GET: api/values
[HttpGet]
public async Task<IEnumerable<Users>> Get()
{
return await _context.Users.ToListAsync();
}
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection":<MyConnectionStringGoesHere>
},
index.js (just in case React might be the routing problem)
const baseUrl = document.getElementsByTagName('base')
[0].getAttribute('href');
const rootElement = document.getElementById('root');
ReactDOM.render(
<BrowserRouter basename={baseUrl}>
<App />
</BrowserRouter>,
rootElement);
registerServiceWorker();
Startup.cs (could be potentially problem with HTTP routing in Prod?)
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
services.AddDbContext<AccrubalanceDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
Conclusion
In conclusion, I need this API call to work within the hosted Azure Web App like it does on my local machine. I know I am close since I got it to work locally, but I am missing something along the way to Azure. Any help or pointers you can provide would be great :)
I am still new to SO and took my time to do my best to format this correctly. I am open to constructive formatting critiques and suggestions to help me improve.
Edit:
As I mentioned before, I am using docker for CD/CI. So I ran my docker container locally and the api does not work there either. Docker throws this warning in the command window when I navigate to the apps home page.
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
Failed to determine the https port for redirect.
Edit 1 Determination
I also found this article which points to react routing being an issue. I have looked in Kudo in my Azure app and I do not have a web.config. Could potentially try adding on but I do not have the regular Windows UI since my app is a Linux server.
The container build acts like the Azure App does, may not be an Azure issue. Still unsure why docker is acting differently than running in VS.
Solution:
There is obviously some problem with Docker. Since it was becoming more of a headache then a help, I removed it from the deployment pipeline and just followed the instructions here. Once I did this deployment method, all the API's worked. Only downside is I had to make a new app in Azure.
I've been trying to perform a GET request from Visual Studio's Android Emulator to an ASP.Net Core API running on localhost. I first read that I have to use IP 10.0.2.2 when performing the request from Android, so I have the following HTTP client code in my Android (Xamarin) App
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView (Resource.Layout.Main);
var logInbutton = FindViewById<Button>(Resource.Id.logInButton);
logInbutton.Click += OnLogInButtonClicked;
}
private async void OnLogInButtonClicked(object sender, EventArgs e)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var uri = new Uri("http://10.0.2.2:5555/api/Orders?api-version=0.9");
var response = await client.GetAsync(uri);
}
}
However, each time I attempt to run this I'm greeted with a 400 Bad Request error. I know the request is okay since I can run the same Http client code from a console app without any errors. I then read that IIS express does not accept external requests and since the Android emulator is running in a VM this could be the reason.
So I then followed the advice on this SO post but was greeted with an Invalid URI: The hostname could not be parsed error. I'm not sure how to proceed from here. Is there a solution to the Invalid URI: The hostname could not be parsed error, or should I attempt to circumvent IIS Express and run solely with kestrel for development? Would even running solely in kestrel fix this problem?
I had a similar issue.
First, let's locate the correct applicationhost.config file.
You should check this location:
[solution folder]/.vs/config/applicationhost.config
Then people advice to set up binding like this:
<binding protocol="http" bindingInformation="*:5555:*" />
It did NOT work for me. I received "Invalid URI: The hostname could not be parsed".
What did work for me is the syntax like this:
<binding protocol="http" bindingInformation=":5555:" />
Please note, port 5555 is given as example. You should use the port your server is listening to.
I ran into the same issue when attempting to connect while running through kestrel. For me it worked using this configuration for the launchsettings:
I just play a little bit with signalR. My application has only one simple hub which is stored in an ASP.NET Application and I wrote a WPF client, which interacts via the hubconnection and the created proxy with the ASP.NET Application. Everything works fine on my local PC. I deployed the ASP.NET Application on IIS.
Now I am getting to the point...
When I type the following into my browser on my own PC (pcthi-and)
http://pcthi-and:8080/signalr/hubs
I'll get what I want
When I type the same url into a browser of another pc I'll get the same response and everything looks fine.
But my Application only works on my pc and not on the other one. When I start the hubconnection on the other pc I don't get a connectionId.
I tried to change the url to my IP-Address without effect.
Browser call to hub works but the Application doesn't work.
The call looks like this:
private bool tryToConnectToCoffeService()
{
try
{
this.hubConnection = new HubConnection(ConfigurationManager.ConnectionStrings["coffeeConnection"].ConnectionString);
this.hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
this.coffeeService = this.hubConnection.CreateHubProxy("coffee");
this.hubConnection.Start();
if (string.IsNullOrEmpty(hubConnection.ConnectionId))
{
return false;
}
return true;
}
catch(Exception ex)
{
return false;
}
}
The Global.asax:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
}
The hub like this
[HubName("coffee")]
public class CoffeeHub : Hub
{
My Hub Connection String is this:
"http://pcthi-and:8080/"
Or:
"http://My-Current-IP-Address:8080/"
I use SignalR 1.0 rc2.
Does anyone have an idea? Thanks for helping.
Cheers
Frank
I think you need to change
hubConnection.Start();
to
hubConnection.Start().Wait();
If you are running .NET 4.5 you could make the tryToConnectToCoffeService method async and then await when you start the hub connection.
await hubConnection.Start();
It likely works today on localhost because the client can finish connecting before if (string.IsNullOrEmpty(hubConnection.ConnectionId)) executes.
It is probably taking longer to connect from another machine which exposes the race condition present when you don't wait for HubConnection.Start() to complete.