Base solution for your next web application

Activities of "GarethWright"

Answer

You can regenerate the same project selecting .NetCore MVC so long as the rest of the detail stays the same

Prerequisites

  • What is your product version? 9.0.1
  • What is your product type (Angular or MVC)? ANGULAR
  • What is product framework type (.net framework or .net core)? .NetCore

Console app gets a token successfully but returns 401 Unauthorised when connecting to the api endpoint from a remote ip. Works fine if connecting via localhost. I think this may be something to do with IdentityServer being configured to use .AddDeveloperSigninCredentials but I have no idea how I would configure this to work in a multi tenant environment where the host is at https://api.{tenant_name}.domain.com

For info:

The issue was because I had used https://www.nuget.org/packages/Hangfire.Dashboard.Customize/ to customise the Hangfire Dashboard.

It's middleware and but doesn't allow any other middleware to run futher down the chain.

Replied to you via email

When I do that I get ERR_TOO_MANY_REDIRECTS

As that folder already contains web.config assume merging the two?

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath=".\projectname.Web.Host.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
    </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: 9fc37c62-2105-4d32-9724-7323b959504b-->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath=".\projectname.Web.Host.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
   
<staticContent>
      <remove fileExtension=".json" />
      <mimeMap fileExtension=".json" mimeType="application/json" />
	  <mimeMap fileExtension="woff" mimeType="application/font-woff" />
      <mimeMap fileExtension="woff2" mimeType="application/font-woff" /> 
    </staticContent>
    <!-- IIS URL Rewrite for Angular routes -->
    <rewrite>
      <rules>
        <rule name="Angular Routes" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
        <defaultDocument>
            <files>
                <clear />
                <add value="index.html" />
            </files>
        </defaultDocument>
 </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: 9fc37c62-2105-4d32-9724-7323b959504b-->

web.config in wwwroot dir

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <staticContent>
      <remove fileExtension=".json" />
      <mimeMap fileExtension=".json" mimeType="application/json" />
	  <mimeMap fileExtension="woff" mimeType="application/font-woff" />
      <mimeMap fileExtension="woff2" mimeType="application/font-woff" /> 
    </staticContent>
    <!-- IIS URL Rewrite for Angular routes -->
    <rewrite>
      <rules>
        <rule name="Angular Routes" enabled="true" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
        <defaultDocument>
            <files>
                <clear />
                <add value="index.html" />
            </files>
        </defaultDocument>
  </system.webServer>
</configuration>

.net core Angular Single Project 9.0.1

This works fine locally but when publishing to IIS any deep link i.e /account/login returns a 404.

It should all as the provided

app.Use(async (context, next) => { await next(); if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) { context.Request.Path = "/index.html"; await next(); 
            
            } });

should handle those 404s

namespace PROJECT.Web.Startup { public class Startup { private const string DefaultCorsPolicyName = "localhost";

    private readonly IConfigurationRoot _appConfiguration;
    private readonly IWebHostEnvironment _hostingEnvironment;

    public Startup(IWebHostEnvironment env)
    {
        _hostingEnvironment = env;
        _appConfiguration = env.GetAppConfiguration();
    }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        //MVC
        services.AddControllersWithViews(options =>
        {
            options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
        }).AddNewtonsoftJson();

        services.AddSignalR();

        //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(
                        // App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
                        _appConfiguration["App:CorsOrigins"]
                            .Split(",", StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.RemovePostFix("/"))
                            .ToArray()
                    )
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });

        if (bool.Parse(_appConfiguration["KestrelServer:IsEnabled"]))
        {
            ConfigureKestrel(services);
        }

        IdentityRegistrar.Register(services);
        AuthConfigurer.Configure(services, _appConfiguration);

        //Identity server
        if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
        {
            IdentityServerRegistrar.Register(services, _appConfiguration, options =>
                 options.UserInteraction = new UserInteractionOptions()
                 {
                     LoginUrl = "/UI/Login",
                     LogoutUrl = "/UI/LogOut",
                     ErrorUrl = "/Error"
                 });
        }

        if (WebConsts.SwaggerUiEnabled)
        {
            //Swagger - Enable this line and the related lines in Configure method to enable swagger UI
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo() { Title = "inteliProCS API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);
                options.ParameterFilter&lt;SwaggerEnumParameterFilter&gt;();
                options.SchemaFilter&lt;SwaggerEnumSchemaFilter&gt;();
                options.OperationFilter&lt;SwaggerOperationIdFilter&gt;();
                options.OperationFilter&lt;SwaggerOperationFilter&gt;();
                options.CustomDefaultSchemaIdSelector();
            }).AddSwaggerGenNewtonsoftSupport();
        }

        //Recaptcha
        services.AddreCAPTCHAV3(x =>
        {
            x.SiteKey = _appConfiguration["Recaptcha:SiteKey"];
            x.SiteSecret = _appConfiguration["Recaptcha:SecretKey"];
        });

        if (WebConsts.HangfireDashboardEnabled)
        {

            //Hangfire(Enable to use Hangfire instead of default job manager)
            services.AddHangfire(config =>
            {
                config.UseSqlServerStorage(_appConfiguration.GetConnectionString("Default"));

            });
        }

        if (WebConsts.GraphQL.Enabled)
        {
            services.AddAndConfigureGraphQL();
        }

        if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
        {
            services.AddAbpZeroHealthCheck();

            var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");

            if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
            {
                services.Configure&lt;HealthChecksUISettings&gt;(settings =>
                {
                    healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
                });
                services.AddHealthChecksUI();
            }
        }

        //Configure Abp and Dependency Injection
        return services.AddAbp&lt;inteliProCSWebHostModule&gt;(options =>
        {
            //Configure Log4Net logging
            options.IocManager.IocContainer.AddFacility&lt;LoggingFacility&gt;(
                f => f.UseAbpLog4Net().WithConfig(_hostingEnvironment.IsDevelopment()
                        ? "log4net.config"
                        : "log4net.Production.config")
            );

            options.PlugInSources.AddFolder(Path.Combine(_hostingEnvironment.WebRootPath, "Plugins"), SearchOption.AllDirectories);
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
    {
        //Initializes ABP framework.
        app.UseAbp(options =>
        {
            options.UseAbpRequestLocalization = false; //used below: UseAbpRequestLocalization
        });

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseStatusCodePagesWithRedirects("~/Error?statusCode={0}");
            app.UseExceptionHandler("/Error");
        }

        app.Use(async (context, next) => { await next(); if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) { context.Request.Path = "/index.html"; await next(); 
        
        } });
      // app.UseDefaultFiles(new DefaultFilesOptions { DefaultFileNames = new List&lt;string&gt; { "index.html" }});
       app.UseStaticFiles();
       app.UseRouting();

        app.UseCors(DefaultCorsPolicyName); //Enable CORS!

        app.UseAuthentication();
        app.UseJwtTokenMiddleware();

        if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
        {
            app.UseJwtTokenMiddleware("IdentityBearer");
            app.UseIdentityServer();
        }

        app.UseAuthorization();

        using (var scope = app.ApplicationServices.CreateScope())
        {
            if (scope.ServiceProvider.GetService&lt;DatabaseCheckHelper&gt;().Exist(_appConfiguration["ConnectionStrings:Default"]))
            {
                app.UseAbpRequestLocalization();
            }
        }

        if (WebConsts.HangfireDashboardEnabled)
        {
            //Hangfire dashboard &server(Enable to use Hangfire instead of default job manager)
            app.UseHangfireDashboard(WebConsts.HangfireDashboardEndPoint, new DashboardOptions
            {

                // Authorization = new[] { new AbpHangfireAuthorizationFilter(AppPermissions.Pages_Administration_HangfireDashboard) }
            });

            var options = new BackgroundJobServerOptions
            {
                Queues = new[] { "WindowsService", "default" }
            };
            app.UseHangfireServer(options);


            RecurringJob.AddOrUpdate&lt;IHangFireNotificationAppService&gt;("Daily CSCS/ ECS Card Check", dd => dd.CheckCSCSECSCardValid(), Cron.Daily(8));
            RecurringJob.AddOrUpdate&lt;IHangFireAppService&gt;("Check access revocation", dd => dd.CheckForAccessRevocation(), "*/5 * * * *");
            RecurringJob.AddOrUpdate&lt;IHangFireAppService&gt;("Monthly Co2Report", mm => mm.MonthlyCo2ReportJob(), Cron.Daily(21));
            RecurringJob.AddOrUpdate&lt;IHangFireAppService&gt;("Weekly Co2Report", ww => ww.WeeklyCo2ReportJob(), Cron.Weekly(DayOfWeek.Friday, 21));
            RecurringJob.AddOrUpdate&lt;IHangFireAppService&gt;("Daily Co2Report", dd => dd.DailyCo2ReportJob(), Cron.Daily(21));
            RecurringJob.AddOrUpdate&lt;IHangFireAppService&gt;("Get Job Request", rs => rs.GetJobRequestResult(), "*/20 * * * *");
            RecurringJob.AddOrUpdate&lt;IHangFireNotificationAppService&gt;
                ("Send Warning Notification", r => r.SendNotificationForHangFire(),
                "*/10 * * * *");

         
        }

        if (bool.Parse(_appConfiguration["Payment:Stripe:IsActive"]))
        {
            StripeConfiguration.ApiKey = _appConfiguration["Payment:Stripe:SecretKey"];
        }

       

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub&lt;AbpCommonHub&gt;("/signalr");
            endpoints.MapHub&lt;ChatHub&gt;("/signalr-chat");

            endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
            endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

            if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                endpoints.MapHealthChecks("/health", new HealthCheckOptions()
                {
                    Predicate = _ => true,
                    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                });
            }
        });

        if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
        {
            if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
            {
                app.UseHealthChecksUI();
            }
        }

        if (WebConsts.SwaggerUiEnabled)
        {
            // Enable middleware to serve generated Swagger as a JSON endpoint
            app.UseSwagger();
            // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)

            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint(_appConfiguration["App:SwaggerEndPoint"], "API V1");
                options.IndexStream = () => Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("inteliProCS.Web.wwwroot.swagger.ui.index.html");
                options.InjectBaseUrl(_appConfiguration["App:ServerRootAddress"]);
            }); //URL: /swagger
        }
    }

    private void ConfigureKestrel(IServiceCollection services)
    {
        services.Configure&lt;Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions&gt;(options =>
        {
            options.Listen(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 443),
                listenOptions =>
                {
                    var certPassword = _appConfiguration.GetValue&lt;string&gt;("Kestrel:Certificates:Default:Password");
                    var certPath = _appConfiguration.GetValue&lt;string&gt;("Kestrel:Certificates:Default:Path");
                    var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(certPath, certPassword);
                    listenOptions.UseHttps(new HttpsConnectionAdapterOptions()
                    {
                        ServerCertificate = cert
                    });
                });
        });
    }
}

}

I tried the web.config

This doesn't work either

I couldn't get it to work. Ended up creating a public facing unauthenticated controller to handle incoming webhooks.

Showing 1 to 10 of 17 entries