How to override express js default Keep Alive behavior - c#

I'm been doing some stress testing on nodejs express server and noticed that it includes a "Connection: Keep-Alive" header by default. My application will be a web api exposed to the client, so I don't need the connection to remain open after the client has posted it's data and received it's response.
Unfortunately due to client limitations this needs to be a synchronous operation. We already have a C# Web API that we intend to replace by a nodejs server and this API don't a append a Keep Alive header on the HTTP response.
I can't find a way to override this behavior. I've tried using app.disable and also removing the header on the middleware, but it doesn't work (it works for other HTTP headers though).
var express = require('express'),
app = express(),
port = 58458;
app.configure(function(){
app.use(express.bodyParser());
app.use(app.router);
});
app.use(function (req, res, next) {
res.removeHeader('Connection','Close');
next();
});
app.disable('Connection');
var counter = 0;
app.listen(port, function() { console.log('escutando') } );
app.post("/api/order", function(req, res) {
counter = counter + 1;
var r = req.body;
if (counter%100 === 0)
console.log(counter);
res.json(1);
res.end();
});

You have to set the header instead of remove Header
app.use(function(req, res, next) {
res.setHeader('Connection', 'close');
next();
});

Related

WebView2 in WebUI3: how to disable CORS for fetch requests?

I have a simple web app I would like to run in a WinUI app that can fetch external HTML content:
private async fetchHtml() {
const url = document.querySelector<HTMLInputElement>("#txt-url")!.value;
const html = await fetch(url).then(r => r.text());
document.querySelector<HTMLTextAreaElement>("#txt-html")!.value = html;
}
Obviously this would hit a CORS error. I want to disable it but cannot find any way for WinUI3:
WebView2 in WinUI does not have EnsureCoreWebView2Async overload that takes CoreWebView2EnvironmentOptions so this answer does not help.
The URL is external URL so SetVirtualHostNameToFolderMapping suggested by this answer does not help as well.
I tried injecting my own CORS header but it does not work on fetch (WebResourceResponseReceived event is not fired only on fetch requests):
c.WebResourceResponseReceived += (_, e) =>
{
var origin = e.Request.Headers.FirstOrDefault(q => q.Key == "Origin").Value;
if (!string.IsNullOrEmpty(origin))
{
e.Response.Headers.AppendHeader("Access-Control-Allow-Origin",
new Uri(e.Request.Uri).GetLeftPart(UriPartial.Authority));
e.Response.Headers.AppendHeader("Access-Control-Allow-Methods", "*");
e.Response.Headers.AppendHeader("Access-Control-Allow-Headers", "*");
}
};
So it turns out you actually need to handle proxying the request in the WebResourceRequested event. WebResourceResponseReceived is triggered after the browser handles the request.
To proxy the request manually, you need to first set up a web resource requested filter.
// CoreWebView2WebResourceContext.XmlHttpRequest is triggered
// by fetches for some reason. This filter will listen for any URI.
this.webView.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.XmlHttpRequest);
Then you can set up a listener just like for WebResourceResponseReceived
this.webView.CoreWebView2.WebResourceRequested += this.CoreWebView2_WebResourceRequested;
So to proxy the request, you need to set up an HttpClient and handle the requests manually.
For example, this will proxy all requests, assuming httpClient is defined as an HttpClient:
private void CoreWebView2_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
{
HttpRequestMessage request = new(new HttpMethod(e.Request.Method), e.Request.Uri)
{
Content = new StreamContent(e.Request.Content)
};
foreach (KeyValuePair<string, string> header in e.Request.Headers)
{
// Copy base headers
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
// Copy content headers
request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
// Make the request
HttpResponseMessage response = httpClient.Send(request);
// Custom headers
response.Headers.Add("Access-Control-Allow-Origin", "*");
// Webview2 Response expecting headers in a string with following format:
// key: value
// header2: value
List<string> headers = response.Headers.Select(h => $"{h.Key}: {string.Join(",", h.Value)}").ToList();
// Create Webview2 response
CoreWebView2WebResourceResponse webView2WebResourceResponse = this.webView.CoreWebView2.Environment.CreateWebResourceResponse(
response.Content.ReadAsStream(),
(int)response.StatusCode,
response.ReasonPhrase,
string.Join('\n', headers));
e.Response = webView2WebResourceResponse;
}
NOTE
Should not use async methods when building out a response object. The COM layer does not respect threading.

Why is my header data missing from my Azure Function Http Trigger in .Net 5 when calling from HttpClient.GetAsync

I have a client using HttpClient.GetAsync to call into a Azure Function Http Trigger in .Net 5.
When I call the function using PostMan, I get my custom header data.
However, when I try to access my response object (HttpResponseMessage) that is returned from HttpClient.GetAsync, my header data empty.
I have my Content data and my Status Code. But my custom header data are missing.
Any insight would be appreciated since I have looking at this for hours.
Thanks for you help.
Edit: Here is the code where I am making the http call:
public async Task<HttpResponseMessage> GetQuotesAsync(int? pageNo, int? pageSize, string searchText)
{
var requestUri = $"{RequestUri.Quotes}?pageNo={pageNo}&pageSize={pageSize}&searchText={searchText}";
return await _httpClient.GetAsync(requestUri);
}
Edit 8/8/2021: See my comment below. The issue has something to do with using Blazor Wasm Client.
For anyone having problems after following the tips on this page, go back and check the configuration in the host.json file. you need the Access-Control-Expose-Headers set to * or they won't be send even if you add them. Note: I added the "extensions" node below and removed my logging settings for clarity.
host.json (sample file):
{
"version": "2.0",
"extensions": {
"http": {
"customHeaders": {
"Access-Control-Expose-Headers": "*"
}
}
}
}
This is because HttpResponseMessage's Headers property data type is HttpResponseHeaders but HttpResponseData's Headers property data type is HttpHeadersCollection. Since, they are different, HttpResponseHeaders could not bind to HttpHeadersCollection while calling HttpClient.GetAsync(as it returns HttpResponseMessage).
I could not find a way to read HttpHeadersCollection through HttpClient.
As long as your Azure function code is emitting the header value, you should be able to read that in your client code from the Headers collection of HttpResponseMessage. Nothing in your azure function (which is your remote endpoint you are calling) makes it any different. Remember, your client code has no idea how your remote endpoint is implemented. Today it is azure functions, tomorrow it may be a full blown aspnet core web api or a REST endpoint written in Node.js. Your client code does not care. All it cares is whether the Http response it received has your expected header.
Asumming you have an azure function like this where you are adding a header called total-count to the response.
[Function("quotes")]
public static async Task<HttpResponseData> RunAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("Quotes");
logger.LogInformation("C# HTTP trigger function processed a request for Quotes.");
var quotes = new List<Quote>
{
new Quote { Text = "Hello", ViewCount = 100},
new Quote { Text = "Azure Functions", ViewCount = 200}
};
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("total-count", quotes.Count.ToString());
await response.WriteAsJsonAsync(quotes);
return response;
}
Your existing client code should work as long as you read the Headers property.
public static async Task<HttpResponseMessage> GetQuotesAsync()
{
var requestUri = "https://shkr-playground.azurewebsites.net/api/quotes";
return await _httpClient.GetAsync(requestUri);
}
Now your GetQuotesAsync method can be called somewhere else where you will use the return value of it (HttpResponseMessage instance) and read the headers. In the below example, I am reading that value and adding to a string variable. HttpResponseMessage implements IDisposable. So I am using a using construct to implicitly call the Dispose method.
var msg = "Total count from response headers:";
using (var httpResponseMsg = await GetQuotesAsync())
{
if (httpResponseMsg.Headers.TryGetValues("total-count", out var values))
{
msg += values.FirstOrDefault();
}
}
// TODO: use "msg" variable as needed.
The types which Azure function uses for dealing with response headers is more of an implementation concern of azure functions. It has no impact on your client code where you are using HttpClient and HttpResponseMessage. Your client code is simply dealing with standard http call response (response headers and body)
The issue is not with Blazor WASM, rather if that header has been exposed on your API Side. In your azure function, add the following -
Note: Postman will still show the headers even if you don't expose the headers like below. That's because, Postman doesn't care about CORS headers. CORS is just a browser concept and not a strong security mechanism. It allows you to restrict which other web apps may use your backend resources and that's all.
First create a Startup File to inject the HttpContextAccessor
Package Required: Microsoft.Azure.Functions.Extensions
[assembly: FunctionsStartup(typeof(FuncAppName.Startup))]
namespace FuncAppName
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<HttpContextAccessor>();
}
}
}
Next, inject it into your main Function -
using Microsoft.AspNetCore.Http;
namespace FuncAppName
{
public class SomeFunction
{
private readonly HttpContext _httpContext;
public SomeFunction(HttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
[FunctionName("SomeFunc")]
public override Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, new[] { "post" }, Route = "run")] HttpRequest req)
{
var response = "Some Response"
_httpContext.Response.Headers.Add("my-custom-header", "some-custom-value");
_httpContext.Response.Headers.Add("my-other-header", "some-other-value");
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "my-custom-header, my-other-header");
return new OkObjectResult(response)
}
If you want to allow all headers you can use wildcard (I think, not tested) -
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "*");
You still need to add your web-app url to the azure platform CORS. You can add * wildcard, more info here - https://iotespresso.com/allowing-all-cross-origin-requests-azure-functions/
to enable CORS for Local Apps during development - https://stackoverflow.com/a/60109518/9276081
Now to access those headers in your Blazor WASM, as an e.g. you can -
protected override async Task OnInitializedAsync()
{
var content = JsonContent.Create(new { query = "" });
using (var client = new HttpClient())
{
var result = await client.PostAsync("https://func-app-name.azurewebsites.net/api/run", content);
var headers = result.Headers.ToList();
}
}

How to change base url of Swagger in .core depend on the request

In the old version of Swagger (.Net framework) I've used this function to change the URL:
RootUrl(req => ComputeHostAsSeenByOriginalClient(req))
Actually I calculate the base URL from the request (my app is behind a load balancer)
The new .core way is to use this RoutePrefix
The problem:
RoutePrefix is Property and not an Action, so I don't have the HttpRequestMessage
This is the ComputeHostAsSeenByOriginalClient full code:
public static string ComputeHostAsSeenByOriginalClient(HttpRequestMessage req)
{
var authority = req.RequestUri.Authority;
var scheme = req.RequestUri.Scheme;
if (req.Headers.Contains("X-Forwarded-Host"))
{
//we are behind a reverse proxy, use the host that was used by the client
var xForwardedHost = req.Headers.GetValues("X-Forwarded-Host").First();
//when multiple apache httpd are chained, each proxy append to the header
//with a comma (see //https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers).
//so we need to take only the first host because it is the host that was
//requested by the original client.
//note that other reverse proxies may behave differently but
//we are not taking care of them...
var firstForwardedHost = xForwardedHost.Split(',')[0];
authority = firstForwardedHost;
}
if (req.Headers.Contains("X-Forwarded-Proto"))
{
//now that we have the host, we also need to determine the protocol used by the
//original client.
//if present, we are using the de facto standard header X-Forwarded-Proto
//otherwise, we fallback to http
//note that this is extremely brittle, either because the first proxy
//can "forget" to set the header or because another proxy can rewrite it...
var xForwardedProto = req.Headers.GetValues("X-Forwarded-Proto").First();
if (xForwardedProto.IndexOf(",") != -1)
{
//when multiple apache, X-Forwarded-Proto is also multiple ...
xForwardedProto = xForwardedProto.Split(',')[0];
}
scheme = xForwardedProto;
}
//no reverse proxy mean we can directly use the RequestUri
return scheme + "://" + authority;
}
Any idea how to workaround this?
Try to use this.Request. You could access the request object from Controller.
public string ComputeHostAsSeenByOriginalClient()
{
var req = this.Request;
var scheme = req.Scheme;
var authority = this.Request.Host.Value.ToString();
if (req.Headers.ContainsKey("X-Forwarded-Host"))
{
//we are behind a reverse proxy, use the host that was used by the client
StringValues xForwardedHost = "";
if (req.Headers.TryGetValue("X-Forwarded-Host", out xForwardedHost))
{
//when multiple apache httpd are chained, each proxy append to the header
//with a comma (see //https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers).
//so we need to take only the first host because it is the host that was
//requested by the original client.
//note that other reverse proxies may behave differently but
//we are not taking care of them...
var firstForwardedHost = xForwardedHost.First().ToString().Split(',')[0];
authority = firstForwardedHost;
}
}
if (req.Headers.ContainsKey("X-Forwarded-Proto"))
{
//now that we have the host, we also need to determine the protocol used by the
//original client.
//if present, we are using the de facto standard header X-Forwarded-Proto
//otherwise, we fallback to http
//note that this is extremely brittle, either because the first proxy
//can "forget" to set the header or because another proxy can rewrite it...
StringValues xForwardedProto = "";
if (req.Headers.TryGetValue("X-Forwarded-Proto", out xForwardedProto))
{
if (xForwardedProto.First().ToString().IndexOf(",") != -1)
{
//when multiple apache, X-Forwarded-Proto is also multiple ...
xForwardedProto = xForwardedProto.First().ToString().Split(',')[0];
}
scheme = xForwardedProto;
}
}
//no reverse proxy mean we can directly use the RequestUri
return scheme + "://" + authority;
}

Returning an HttpResponseMessage contents from a pass through API in ASP.NET Core [duplicate]

I'm developing an ASP.Net Core web application where I need to create a kind of "authentication proxy" to another (external) web service.
What I mean by authentication proxy is that I will receive requests through a specific path of my web app and will have to check the headers of those requests for an authentication token that I'll have issued earlier, and then redirect all the requests with the same request string / content to an external web API which my app will authenticate with through HTTP Basic auth.
Here's the whole process in pseudo-code
Client requests a token by making a POST to a unique URL that I sent him earlier
My app sends him a unique token in response to this POST
Client makes a GET request to a specific URL of my app, say /extapi and adds the auth-token in the HTTP header
My app gets the request, checks that the auth-token is present and valid
My app does the same request to the external web API and authenticates the request using BASIC authentication
My app receives the result from the request and sends it back to the client
Here's what I have for now. It seems to be working fine, but I'm wondering if it's really the way this should be done or if there isn't a more elegant or better solution to this? Could that solution create issues in the long run for scaling the application?
[HttpGet]
public async Task GetStatement()
{
//TODO check for token presence and reject if issue
var queryString = Request.QueryString;
var response = await _httpClient.GetAsync(queryString.Value);
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
[HttpPost]
public async Task PostStatement()
{
using (var streamContent = new StreamContent(Request.Body))
{
//TODO check for token presence and reject if issue
var response = await _httpClient.PostAsync(string.Empty, streamContent);
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType?.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
}
_httpClient being a HttpClient class instantiated somewhere else and being a singleton and with a BaseAddressof http://someexternalapp.com/api/
Also, is there a simpler approach for the token creation / token check than doing it manually?
If anyone is interested, I took the Microsoft.AspNetCore.Proxy code and made it a little better with middleware.
Check it out here: https://github.com/twitchax/AspNetCore.Proxy. NuGet here: https://www.nuget.org/packages/AspNetCore.Proxy/. Microsoft archived the other one mentioned in this post, and I plan on responding to any issues on this project.
Basically, it makes reverse proxying another web server a lot easier by allowing you to use attributes on methods that take a route with args and compute the proxied address.
[ProxyRoute("api/searchgoogle/{query}")]
public static Task<string> SearchGoogleProxy(string query)
{
// Get the proxied address.
return Task.FromResult($"https://www.google.com/search?q={query}");
}
I ended up implementing a proxy middleware inspired by a project in Asp.Net's GitHub.
It basically implements a middleware that reads the request received, creates a copy from it and sends it back to a configured service, reads the response from the service and sends it back to the caller.
This post talks about writing a simple HTTP proxy logic in C# or ASP.NET Core. And allowing your project to proxy the request to any other URL. It is not about deploying a proxy server for your ASP.NET Core project.
Add the following code anywhere of your project.
public static HttpRequestMessage CreateProxyHttpRequest(this HttpContext context, Uri uri)
{
var request = context.Request;
var requestMessage = new HttpRequestMessage();
var requestMethod = request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(request.Body);
requestMessage.Content = streamContent;
}
// Copy the request headers
foreach (var header in request.Headers)
{
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
requestMessage.Headers.Host = uri.Authority;
requestMessage.RequestUri = uri;
requestMessage.Method = new HttpMethod(request.Method);
return requestMessage;
}
This method covert user sends HttpContext.Request to a reusable HttpRequestMessage. So you can send this message to the target server.
After your target server response, you need to copy the responded HttpResponseMessage to the HttpContext.Response so the user's browser just gets it.
public static async Task CopyProxyHttpResponse(this HttpContext context, HttpResponseMessage responseMessage)
{
if (responseMessage == null)
{
throw new ArgumentNullException(nameof(responseMessage));
}
var response = context.Response;
response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
response.Headers[header.Key] = header.Value.ToArray();
}
// SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
response.Headers.Remove("transfer-encoding");
using (var responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
await responseStream.CopyToAsync(response.Body, _streamCopyBufferSize, context.RequestAborted);
}
}
And now the preparation is complete. Back to our controller:
private readonly HttpClient _client;
public YourController()
{
_client = new HttpClient(new HttpClientHandler()
{
AllowAutoRedirect = false
});
}
public async Task<IActionResult> Rewrite()
{
var request = HttpContext.CreateProxyHttpRequest(new Uri("https://www.google.com"));
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, HttpContext.RequestAborted);
await HttpContext.CopyProxyHttpResponse(response);
return new EmptyResult();
}
And try to access it. It will be proxied to google.com
A nice reverse proxy middleware implementation can also be found here: https://auth0.com/blog/building-a-reverse-proxy-in-dot-net-core/
Note that I replaced this line here
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
with
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToString());
Original headers (e.g. like an authorization header with a bearer token) would not be added without my modification in my case.
I had luck using twitchax's AspNetCore.Proxy NuGet package, but could not get it to work using the ProxyRoute method shown in twitchax's answer. (Could have easily been a mistake on my end.)
Instead I defined the mapping in Statup.cs Configure() method similar to the code below.
app.UseProxy("api/someexternalapp-proxy/{arg1}", async (args) =>
{
string url = "https://someexternalapp.com/" + args["arg1"];
return await Task.FromResult<string>(url);
});
Piggy-backing on James Lawruk's answer https://stackoverflow.com/a/54149906/6596451 to get the twitchax Proxy attribute to work, I was also getting a 404 error until I specified the full route in the ProxyRoute attribute. I had my static route in a separate controller and the relative path from Controller's route was not working.
This worked:
public class ProxyController : Controller
{
[ProxyRoute("api/Proxy/{name}")]
public static Task<string> Get(string name)
{
return Task.FromResult($"http://www.google.com/");
}
}
This does not:
[Route("api/[controller]")]
public class ProxyController : Controller
{
[ProxyRoute("{name}")]
public static Task<string> Get(string name)
{
return Task.FromResult($"http://www.google.com/");
}
}
Hope this helps someone!
Twitchax's answer seems to be the best solution at the moment. In researching this, I found that Microsoft is developing a more robust solution that fits the exact problem the OP was trying to solve.
Repo: https://github.com/microsoft/reverse-proxy
Article for Preview 1 (they actually just released prev 2): https://devblogs.microsoft.com/dotnet/introducing-yarp-preview-1/
From the Article...
YARP is a project to create a reverse proxy server. It started when we noticed a pattern of questions from internal teams at Microsoft who were either building a reverse proxy for their service or had been asking about APIs and technology for building one, so we decided to get them all together to work on a common solution, which has become YARP.
YARP is a reverse proxy toolkit for building fast proxy servers in .NET using the infrastructure from ASP.NET and .NET. The key differentiator for YARP is that it is being designed to be easily customized and tweaked to match the specific needs of each deployment scenario. YARP plugs into the ASP.NET pipeline for handling incoming requests, and then has its own sub-pipeline for performing the steps to proxy the requests to backend servers. Customers can add additional modules, or replace stock modules as needed.
...
YARP works with either .NET Core 3.1 or .NET 5 preview 4 (or later). Download the preview 4 (or greater) of .NET 5 SDK from https://dotnet.microsoft.com/download/dotnet/5.0
More specifically, one of their sample apps implements authentication (as for the OP's original intent)
https://github.com/microsoft/reverse-proxy/blob/master/samples/ReverseProxy.Auth.Sample/Startup.cs
Here is a basic implementation of Proxy library for ASP.NET Core:
This does not implement the authorization but could be useful to someone looking for a simple reverse proxy with ASP.NET Core. We only use this for development stages.
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
namespace Sample.Proxy
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(options =>
{
options.AddDebug();
options.AddConsole(console =>
{
console.IncludeScopes = true;
});
});
services.AddProxy(options =>
{
options.MessageHandler = new HttpClientHandler
{
AllowAutoRedirect = false,
UseCookies = true
};
options.PrepareRequest = (originalRequest, message) =>
{
var host = GetHeaderValue(originalRequest, "X-Forwarded-Host") ?? originalRequest.Host.Host;
var port = GetHeaderValue(originalRequest, "X-Forwarded-Port") ?? originalRequest.Host.Port.Value.ToString(CultureInfo.InvariantCulture);
var prefix = GetHeaderValue(originalRequest, "X-Forwarded-Prefix") ?? originalRequest.PathBase;
message.Headers.Add("X-Forwarded-Host", host);
if (!string.IsNullOrWhiteSpace(port)) message.Headers.Add("X-Forwarded-Port", port);
if (!string.IsNullOrWhiteSpace(prefix)) message.Headers.Add("X-Forwarded-Prefix", prefix);
return Task.FromResult(0);
};
});
}
private static string GetHeaderValue(HttpRequest request, string headerName)
{
return request.Headers.TryGetValue(headerName, out StringValues list) ? list.FirstOrDefault() : null;
}
public void Configure(IApplicationBuilder app)
{
app.UseWebSockets()
.Map("/api", api => api.RunProxy(new Uri("http://localhost:8833")))
.Map("/image", api => api.RunProxy(new Uri("http://localhost:8844")))
.Map("/admin", api => api.RunProxy(new Uri("http://localhost:8822")))
.RunProxy(new Uri("http://localhost:8811"));
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

Signal R Server Client "Access-Control-Allow-Origin" missing error

I'm trying out the Signal R and built a server dll (windows service library/c#) that runs as a Windows Services. I have build also a client application (asp.net web application) to communicate with the server.
But i'm getting always the error(Firefox) "Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%5D&_=1482829095207. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)."
Chrome error "
Failed to load resource: the server responded with a status of 400 (Bad Request)"
XMLHttpRequest cannot load http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%5D&_=1482830200155. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:50259' is therefore not allowed access. The response had HTTP status code 400.
Note: Edge and also IE gives me errors
I have read almost every post about this subject on Stackoverflow, but non of these solutions seems to work.
The code i'm using for the server side:
namespace SignalRService
{
public class StartupConfiguration
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
EnableJSONP = true,
};
map.RunSignalR(hubConfiguration);
});
}
}
}
Services.cs
public void StartService()
{
LogMessage("SignalRService started", true);
Running = true;
WebApp.Start<StartupConfiguration>(ConfigurationManager.AppSettings["SignalRServerUrl"]);
}
EnvironmentSettings.config:
<add key="SignalRServerUrl" value="http://localhost:8080"/>
Hubs.cs
namespace SignalRService.Hubs
{
[HubName("TestHub")]
public class TestHub: Hub
{
public static Dictionary<string, List<HubClient>> clients = new Dictionary<string, List<HubClient>>();
[HubMethodName("Subscribe")]
public async Task Subscribe(string Id)
{...... }}
ClientSide (Javascript/Jquery)
var signalrHubConnection;
var signalrHubConnectionProxy;
var signalRServerUrl = "http://localhost:8080";
var currentTimeout;
var count = 0;
var startSignalRConnection = function () {
console.log("Start");
signalrHubConnection = $.hubConnection(signalRServerUrl);
console.log("Running");
signalrHubConnection.logging = true;
signalrHubConnectionProxy = signalrHubConnection.createHubProxy('TestHub');
console.log("--Subscribe starting");
signalrHubConnection.start()
.done(function () {
signalrHubConnectionProxy.invoke('Subscribe', Id.toString());
console.log("Subscribe ending");
})
.fail(function (test) {
if (count < 5) {
console.log(test.toString());
clearTimeout(currentTimeout);
currentTimeout = setTimeout(function () {
count++;
startSignalRConnection();
}, 300000); // retry after 5 minutes
}
}
);
signalrHubConnectionProxy.on('IncomingMessage',
function (message) {
console.log("Message = " + message.toString());
}
);
};
Test.aspx
<script src="https://code.jquery.com/jquery-3.1.1.min.js" type="text/javascript"></script>
<script src="http://ajax.aspnetcdn.com/ajax/signalr/jquery.signalr-2.2.1.min.js"></script>
Did I something wrong?
The error implied that the SignalR url is different from the requesting url (origin). So, SignalR is on localhost, but your main website (the site that holds the client side example) obviously is accessed using "localhost".
Maybe you're accessing it using an IP (eg http://127.0.0.1/) or your PC name (eg http://badassPC/), whereas they must match under the default SignalR setting. I am pretty certain it doesn't matter if the port is different, and also doesn't matter if they are on the same domain (eg www.mysite.com and signalr.mysite.com)
Note there is a workaround that I wouldn't recommend unless you really really know what you're doing as there is a quite serious security risk otherwise: https://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#crossdomain

Categories