2

I've got some problems concerning CORS/HttpClient in dotnet core. First of all my Application is structured like this:

Webapp -> Microservice-Gateway -> multiple Microservices

If I do an Ajax call from my Webapp

$.ajax({
        url: "https://[actual gateway Url]/customer/" + customerId,
        type: "GET",
        timeout: 60000,
        crossDomain: true,
        dataType: "json",
        success: data => {
            //...
        }
    });

to the Gateway-Server I receive sometimes following error Message:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:50595' is therefore not allowed access. The response had HTTP status code 500.

Internally however my gateway throws this error if I debug:

System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.Http.WinHttpException: The operation timed out

I tried to solve the first error by adding following code in the gateway and every microservice:

public void ConfigureServices(IServiceCollection services) {
        var corsBuilder = new CorsPolicyBuilder();
        corsBuilder.AllowAnyHeader();
        corsBuilder.AllowAnyMethod();
        corsBuilder.AllowAnyOrigin();
        corsBuilder.AllowCredentials();

        services.AddCors(options => {
            options.AddPolicy("SiteCorsPolicy", corsBuilder.Build());
        });
 //..
 }
 public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
        app.UseCors("SiteCorsPolicy");
 //..
 }

And just to be sure I also added

[EnableCors("SiteCorsPolicy")]

to every controller. The second problem I tried to solve by creating an HttpClient as Singleton in my Gateway and modifying following properties:

var client = new HttpClient();
client.Timeout = new TimeSpan(0, 10, 0);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
services.AddSingleton(client);

My gateway sends HTTP-requests like that:

[HttpGet("{id}")]
public async Task<JsonResult> GetById(int id) {
    var response = await _client.GetAsync(new Uri(ServiceUrls.Customer + $"{id}"));

    if (!response.IsSuccessStatusCode)
        return new JsonResult(BadRequest($"{(int)response.StatusCode}: {response.ReasonPhrase}"));

    var customer = JsonConvert.DeserializeObject<CustomerViewModel>(await response.Content.ReadAsStringAsync());
    return new JsonResult(customer);
}

As I said, sometimes it works and sometimes it doesn't. Maybe you could give me some ideas whats my mistake. Thanks in advance!

Edit: The problem seems to appear more often if there are multiple async ajax calls from the Webapp at once. But this could also be just a feeling.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Simon
  • 81
  • 1
  • 8
  • Can you share where you defined your 'SiteCorsPolicy' policy @Simon? – Anthony Liriano May 09 '18 at 11:37
  • "options.AddPolicy("SiteCorsPolicy", corsBuilder.Build());" in the fifth Code segment – Simon May 09 '18 at 11:40
  • The problem is 500 error, and that’s unrelated to your CORS configuration. It’s just that like most servers, this one only adds the Access-Control-Allow-Origin and other custom-configured response headers to 2xx success messages, not to 5xx and 4xx errors. – sideshowbarker May 09 '18 at 11:59
  • Thanks @sideshowbarker! That narrows it down to the HttpClient. I've read multiple times that HttpClient is thread safe, is that wrong? I can't really think of another reason it should fail. – Simon May 09 '18 at 12:10
  • https://stackoverflow.com/questions/11178220/is-httpclient-safe-to-use-concurrently seems to be the canonical answer about HttpClient thread safety. – sideshowbarker May 09 '18 at 12:20

3 Answers3

5

This probably has nothing to do with your CORS setup. When an error occurs (the HTTP call using HttpClient for example), the default exception handler middleware removes all response headers and returns the error response. This means that any CORS header added by your policy will be removed, therefore you'll receive the error about "No 'Access-Control-Allow-Origin' header is present on the requested resource.".

To overcome this you should handle any exceptions in the controller yourself and return the appropriate error result, or write middleware before or replace the default exception middleware with your own exception handling logic.

There is also an open GitHub issue about this behavior: https://github.com/aspnet/Home/issues/2378.

Henk Mollema
  • 44,194
  • 12
  • 93
  • 104
  • Thank you, I get that. However the "System.Net.Http.WinHttpException: The operation timed out" makes no sense. I upped the Timeout to 10 minutes and I get these Exceptions in like 30 seconds. Maybe the reason is Kestrel not being able to handle 30-50 simultaneous requests. But I dont't really believe that. I forgot to mention that the servers are dockerized. Maybe docker can't handle the amount of connections? – Simon May 09 '18 at 12:50
0

Ok guys,

thanks for getting me into the right direction. I think I found a workaround/fix. I had to set following property in my gateway:

ServicePointManager.DefaultConnectionLimit = int.MaxValue;
Simon
  • 81
  • 1
  • 8
0

I came upon this issue too, .NET Core 2.0 does not return http status codes on non-200 or non-300 responses. I fixed it by creating middlewear to sit in the creationg of the Response to be sent back to the client.

You can take a look at all the steps here: https://medium.com/@arieldiaz92/net-core-2-0-fix-that-http-status-0-error-by-sending-cors-headers-on-failures-12fe3d245107