简体   繁体   中英

Can't get Angular with ASP.Net API project running on Kestrel server using wwwroot as path to Angular project files

INFO - I'm running and debugging my (Angular 10 / ASP.Net 5) site successfully in VS Code connecting to localhost:4200 with >ng serve and >dotnet run, I now want to publish it to Azure, but first I want to test it locally on Kestrel only, by building the Angular project to the wwwroot folder in the.Net API project and running the site from localhost:5001. I build (>ng build) to wwwroot in my API project folder, I see all the Angular files being created in the wwwroot folder, and then I execute >dotnet run.

I see

Now listening on: https://localhost:5001

and then I open the browser to hit that url and I see these errors in the terminal

info: Microsoft.AspNetCore.Hosting.Diagnostics[1] Request starting HTTP/1.1 GET https://localhost:5001/ - - info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed. These requirements were not met: DenyAnonymousAuthorizationRequirement: Requires an authenticated user. info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12] AuthenticationScheme: Bearer was challenged. info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed. These requirements were not met: DenyAnonymousAuthorizationRequirement: Requires an authenticated user. info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12] AuthenticationScheme: Bearer was challenged. info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished HTTP/1.1 GET https://localhost:5001/ - - - 401 0 - 70.3847ms

QUESTION - Is this a configuration issue? A CORS/permission issue? A authentication issue? A folder/path issue?

Here is additional code to help anyone that might be able to help me understand what is going on

Startup.cs

ConfigureServices()

public void ConfigureServices(IServiceCollection services)
    {
        services.AddAutoMapper(typeof(MappingEvents));
        services.AddAutoMapper(typeof(MappingMembers));
        services.AddAutoMapper(typeof(MappingUsers));
        services.AddAutoMapper(typeof(MappingYogabands));
        services.AddAutoMapper(typeof(MappingReviews));
        // services.AddControllers();
        services.AddControllers()
            .AddNewtonsoftJson(opt => 
            {
                opt.SerializerSettings.ReferenceLoopHandling =
                     Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            });


        services.AddDbContext<DataContext>(x =>
            x.UseSqlServer(_config.GetConnectionString("SqlServerConnection"), y => y.UseNetTopologySuite()));

        services.AddTransient<IEmailSender, EmailSender>();
        services.Configure<AuthMessageSenderOptions>(_config.GetSection("SendGrid"));
        services.Configure<ConfirmationOptions>(_config.GetSection("Confirmation"));

        services.Configure<CloudinarySettings>(_config.GetSection("CloudinarySettings"));
        
        services.AddApplicationServices();
        services.AddIdentityServices(_config);
        services.AddSwaggerDocumentation();


        // telling out client app, that if it's running on an unsecure port, we won't return a header, 
        // that will allow our browser to display that information
        // * only allowed to access info from localhost:4200, all others will be denied?
        services.AddCors(opt => 
        {
            opt.AddPolicy("CorsPolicy", policy => 
            {
                policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("https://localhost:4200");
            });
        });
    }

Configure()

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseMiddleware<ExceptionMiddleware>();

        // when coming into the server and we don't have an endpoint that matches the request, then we hit this below
        // it will then re direct to our error controller, pass in the status code and return an object result
        app.UseStatusCodePagesWithReExecute("/errors/{0}"); // after creating ErrorController

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors("CorsPolicy");

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseSwaggerDocumention();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireAuthorization();
            // endpoints.MapControllers();
            endpoints.MapFallbackToController("Index", "Fallback");
        });
    }

AddIdentityService() (called in ConfigureServices())

public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config)
    {
        services.Configure<IdentityOptions>(options =>
        {
            options.User.RequireUniqueEmail = true;
            options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@";
        });

        var builder = services.AddIdentityCore<User>(opt =>
        {
            opt.Password.RequireDigit = false;
            opt.Password.RequiredLength = 2;
            opt.Password.RequireNonAlphanumeric = false;
            opt.Password.RequireUppercase = false;
            // *******************************************************
            // for email confirmation
            opt.SignIn.RequireConfirmedEmail = true;
            // *******************************************************
        });

        builder = new IdentityBuilder(builder.UserType, typeof(Role), builder.Services);
        // AddDefaultTokenProviders() allows the ability to create a token to send to user when they forgot password
        builder.AddEntityFrameworkStores<DataContext>().AddDefaultTokenProviders();
        
        // for roles
        builder.AddRoleValidator<RoleValidator<Role>>();
        builder.AddRoleManager<RoleManager<Role>>();

        builder.AddSignInManager<SignInManager<User>>();

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options => 
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Token:Key"])),
                    ValidIssuer = config["Token:Issuer"],
                    ValidateIssuer = true,
                    ValidateAudience = false
                };
            });

        services.AddAuthorization(options =>
        {
            options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin"));
            options.AddPolicy("ModeratePhotoRole", policy => policy.RequireRole("Admin", "Moderator"));
            options.AddPolicy("VipOnly", policy => policy.RequireRole("VIP"));
        });

        return services;
    }

build section of Angular.json file

"build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {
        "allowedCommonJsDependencies": ["angular2-wizard", "hammerjs"],
        "outputPath": "../API/wwwroot",
        "index": "src/index.html",
        "main": "src/main.ts",
        "polyfills": "src/polyfills.ts",
        "tsConfig": "tsconfig.app.json",
        "aot": true,
        "assets": [
          "src/favicon.ico",
          "src/assets"
        ],
        "styles": [
          "src/styles.scss"
        ],
        "scripts": []
      },
      "configurations": {
        "production": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/environment.prod.ts"
            }
          ],
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "extractCss": true,
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "2mb",
              "maximumError": "5mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "6kb",
              "maximumError": "10kb"
            }
          ]
        }
      }
    }

Environment.ts in my Angular project

 export const environment = { production: false, apiUrl: 'https://localhost:5001/api/' };

I fixed my problem. Here is what I did.

  1. added this line to Configure() in Startup.cs

    app.UseStaticFiles();

  2. Removed

    .RequireAuthorization() from

    endpoints.MapControllers().RequireAuthorization();

Now it's working fine, running my Ng app from wwwroot in my API project

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM