Open Closed

CORS in Angular and .NET CORE to call external services. #3448


0
justinp created

I am working to add some functionality to validate connections to customer systems. I have a basic form where the user puts in a valid URI and clicks a button to test the connection.

This is the Angular4+.NET Core version of ABP, so the connection string is passed to a webservice in the API called testConnection(URI connectionString). The application tries a connection and right now only returns a string of "Success" or "Error." I also have a breakpoint set to watch execution.

When I call the webservice via swagger, the breakpoint is hit, the connection is successful and I get the expected result. When I call the webservice via the Angular front end, the breakpoint is hit, the connection is also successful, but I get a 500 with the error:

XMLHttpRequest cannot load <a class="postlink" href="http://localhost:22742/api/services/app/Connections/TestConnection?connectionString=http%3A%2F%2Fspark.furore.com%2Ffhir&">http://localhost:22742/api/services/app ... om%2Ffhir&</a>. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 500.

This is a classic CORS error. However, I cannot figure out how to enable this type of functionality within ABPZ. I tried changing this line in the Startup.cs:

.WithOrigins(_appConfiguration["App:CorsOrigins"].Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.RemovePostFix("/")).ToArray())

to this:

.AllowAnyOrigin();

but that did not fix the issue. Any help is much appreciated. <a class="postlink" href="https://stackoverflow.com/questions/40043097/cors-in-net-core">https://stackoverflow.com/questions/400 ... n-net-core</a>


13 Answer(s)
  • 0
    justinp created

    Other thing's I've tried, but still it is not working.

    1. Adding "http:/" or just "" to the appsettings.json file
    "CorsOrigins": "http://localhost:4200,http://localhost:49152,http://*,*"
    
    1. Adding an additional policy within Startup.cs and then adding that policy to the web service. Note, this requires adding Microsoft.AspNetCore.Cors NuGet package to the Application project. I was basically following the Microsoft documentation here (<a class="postlink" href="https://docs.microsoft.com/en-us/aspnet/core/security/cors">https://docs.microsoft.com/en-us/aspnet ... urity/cors</a>). Policy definition:
    //Configure CORS for angular2 UI
                services.AddCors(options =>
                {
                    options.AddPolicy(DefaultCorsPolicyName, builder =>
                    {
                        //App:CorsOrigins in appsettings.json can contain more than one address with splitted by comma.
                        builder
                            .WithOrigins(_appConfiguration["App:CorsOrigins"].Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.RemovePostFix("/")).ToArray())
                            .AllowAnyHeader()
                            .AllowAnyMethod();
                    });
                    options.AddPolicy("TestConnectionPolicy", builder => 
                    {
                        builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
                    });
                });
    

    And here's the policy declaration on the service.

    using Microsoft.AspNetCore.Cors;
    
    namespace Company.Project.Connections
    {
        [EnableCors("TestConnectionPolicy")]
        public class ConnectionsAppService : ApplicationService, IConnectionsAppService
        {
    

    What really throws me is the error specifies <a class="postlink" href="http://localhost:4200">http://localhost:4200</a>, which **IS**in the appsettings.json file.

  • 0
    justinp created

    Before I step away from this issue, I wanted to provide the header that is returned. As the error indicates, there is "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 500."

    Here is the header with the error:

    POST /api/services/app/Connections/TestConnection?connectionString=http%3A%2F%2Fspark.furore.com%2Ffhir& HTTP/1.1
    Host: localhost:22742
    Connection: keep-alive
    Content-Length: 0
    **Origin: http://localhost:4200**
    Abp.TenantId: 1
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjIiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6IjI2YzRhNzE3LTEzYzYtZWY3Ni1kMjQzLTM5ZGYzZGI3NDY5NSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluIiwiaHR0cDovL3d3dy5hc3BuZXRib2lsZXJwbGF0ZS5jb20vaWRlbnRpdHkvY2xhaW1zL3RlbmFudElkIjoiMSIsInN1YiI6IjIiLCJqdGkiOiIxZDY0ZTM1YS1kMmUzLTRlNTItODQ4NC1lYzU3Yzk0MjM2YTIiLCJpYXQiOjE0OTgzNjg3NDksIm5iZiI6MTQ5ODM2ODc0OSwiZXhwIjoxNDk4NDU1MTQ5LCJpc3MiOiJhcG9sbG8iLCJhdWQiOiJhcG9sbG8ifQ.BobvLtaes8XAFpFQU5FGKr9QBCALF1Mx2HSj7GYfnVo
    Content-Type: application/json; charset=UTF-8
    Accept-Language: en
    Accept: application/json; charset=UTF-8
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
    Referer: http://localhost:4200/app/admin/connections
    Accept-Encoding: gzip, deflate, br
    

    I am not a CORS expert, but the default ABP policy defined explicity defines <a class="postlink" href="http://localhost:4200">http://localhost:4200</a>, and trying to allow all origins doesn't give the proper header in response.

  • 0
    ismcagdas created
    Support Team

    Hi @JustinP,

    Sorry for the late response. I assume you are trying to make this requet from AspNet Zero's angular app, right ? Other functionality is working withut CORS error I think. If so, there must be a special thing for this case.

    AspNet Zero allowes locahost:4200 in it's CorsOrigins by default.

    If you can provide more info, we will try to solve your problem.

    Thanks.

  • 0
    justinp created

    Thank you.

    As I mentioned in my previous post, I am passing the connection string from the Angular App to the .NET Core API, which tests the connection and passes the result back to the Angular App.

    Let me know what else I need to do to enable CORS.

  • 0
    ismcagdas created
    Support Team

    Hi,

    I just didn't understand why did you need a special configuration for ConnectionsAppService ? I assume other app services are accessible from angular UI without CORS problem, right ?

    Do you call this ConnectionsAppService from another client app ? If so, and if it is a web app, you just need to include it's url to CorsOrigins in .Web.Host project's setting file. A syntax like "http://,*" will not work. At least, we couldn't make it run when developing CorsOrigins setting.

    Thanks.

  • 0
    justinp created

    Thanks.

    I don't understand why I need a special configuration either. However I am getting the error, so something must be done.

    Yes, all other app services are accessible from angular UI without CORS.

    No, I am not using another client app. This is a simple form built into the Angular front end.

    Here's how you reproduce using the Angular + Core solution:

    1. Create an ApplicationService with a method that calls an external/public web service and returns a simple string of either "success" or "error" using a try/catch block. I do this so that I receive a string back no matter what happens within the code.
    2. Build/Run the app and run nswag/refresh.bat to update the service proxy class in the Angular solution.
    3. Create a simple form in the Angular solution with a button that calls the service.

    If you set a break point in your web-service call, you will notice that it returns the result without issue. But when received by the browser, you get the error I posted.

  • 0
    alper created
    Support Team

    hi,

    Can you just create a new method that returns a simple string "success" in ConnectionsAppService? Method must be without input parameters and returns just a string. We'll understand if the parameter (connectionString) is the problem or accessing remote webservice is the problem.

  • 0
    justinp created

    I think you are onto something there...

    I created a simple method inside the same ConnectionsApplciationService that accepts no arguments and returns a string, and still get an error when calling from my Angular client!

    public string TestConnectionWithoutPassingConnectionString()
            {
                try
                {              
                    return "SUCCESS!";
                }
                catch (Exception ex) { return "ERROR: " + ex.Message; }
            }
    

    So the issue must be with the ConnectionsApplicationService. However, I have other ApplicationServices that don't have this issue. Here's some more info on the implementation (some I provided earlier but put here again so it is easy to reference).

    ConnectionsApplicationService:

    public class ConnectionsAppService : ApplicationService, IConnectionsAppService
    

    IConnectionsApplicationService:

    public interface IConnectionsAppService : IApplicationService
    

    Again, I have other several other services that inherit from ApplicationService in the same way.

  • 0
    justinp created

    Oh, and I should mention that I have a method called GetConnections that works fine from that service.

  • 0
    justinp created

    I've spent a couple hours looking at several different things and now I don't believe this is a CORS issue.

    What I have discovered is that NONE of the new methods I develop are now working, but old ones are working just fine.

    I tried just creating a service method that accepts no arguments and returns a string. [attachment=2:3q524lsu]TestConnectionWorksFromAPI_BreakPoint.png[/attachment:3q524lsu] This had the same error. But the problem isn't with the service because the same ConnectionsAppService has a GetCustomerConnections that works just fine.

    public class ConnectionsAppService : ApplicationService, IConnectionsAppService
        {
            private readonly IRepository<CustomerConnections> _connections;
    
            public ConnectionsAppService(IRepository<CustomerConnections> context)
            {
                _connections = context;           
            }
    
            public CustomerConnections GetCustomerConnections()
            {
                if (_connections.Count() != 0) { return _connections.GetAll().First(); } //return the first connection found
                else { return new CustomerConnections(); } //or return a new connections setting
            }
    
            public string TestConnection() //<-This is fails when called from the Angular Client, but works from Swagger
            {
                return "Success";
            }       
        }
    

    I run the API, and test the method and it works just fine. [attachment=1:3q524lsu]TestConnectionWorksFromAPI.png[/attachment:3q524lsu] I update the service proxies and call the code from a button on my Angular component.

    testPatientConnection(): void {
            this._connectionsServiceProxy.testConnection().subscribe(
                (result) => { this.notify.success(result); },
                (result) => {  },
                () => { }
            );        
        }
    

    The breakpiont hits, but the error is still there:[attachment=0:3q524lsu]TestConnectionWorksFromAPI_Error.png[/attachment:3q524lsu] I'm losing my mind over this one...

    I then tried to add this same method to a different AppService, even though I know the GetConnections Method works. I injected that service into my component and called the method from the other service. The API breakpoint hit, but I got the same error.

    We're either dealing with some small thing I'm overlooking, or a focused, non-terminal, repeating phantasm or a class-five full-roaming vapor. A real nasty one, too.

  • 0
    justinp created

    And just so I'm not leaving anything out, the default CORS settings were in place for testing:

    services.AddCors(options =>
                {
                    options.AddPolicy(DefaultCorsPolicyName, builder =>
                    {
                        //App:CorsOrigins in appsettings.json can contain more than one address with splitted by comma.
                        builder
                            .WithOrigins(_appConfiguration["App:CorsOrigins"].Split(",", StringSplitOptions.RemoveEmptyEntries).Select(o => o.RemovePostFix("/")).ToArray())
                            .AllowAnyHeader()
                            .AllowAnyMethod();                        
                    });                
                });
    
    "CorsOrigins": "http://localhost:4200,http://localhost:49152"
    
  • 0
    justinp created

    OK. This is officially nonsense. I created a completely new TestAppService with a single GET method that returns a string and it fails with the same error. Something is cached or screwed or something. All my previous service methods work, all normal API calls within the ABP app works.

  • 0
    justinp created

    I've sent an email and a copy of the code.