部署到 IIS 7.5 服务器的 React/dotnetcore 3.1 mvc 应用程序正确加载,但对 API 的 javascript 调用每次返回 index.html

IT技术 javascript c# reactjs .net-core
2021-05-26 07:49:43

我的猜测是我正在处理 IIS 路由问题。

我有一个正在 Visual Studio 中开发的 React/dotnet 核心 3.1 应用程序。加载时,React 应用程序尝试调用应用程序中的 REST 服务来为选择组件(选择组件是公司的下拉列表)拉入数据。

一切都在开发环境中运行良好。当我部署到临时服务器时,所有 API 调用都被重定向到 index.html 并且我从未获得我期望的数据。

我最好的猜测是我在 IIS 7.5 服务器中错误配置了路由,它不知道如何处理对“/api/”的调用

appsettings.json:

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
        DATA REMOVED HERE
    }
}

程序.cs:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args)
            .Build()
            .Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

启动.cs:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {

        services.AddControllersWithViews();

        // In production, the React files will be served from this directory
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/build";
        });



        // DI
        services.AddSingleton<IConfiguration>(this.Configuration);

        /*services.Configure<IISServerOptions>(options =>
        {
            options.AutomaticAuthentication = false;
        });*/

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseSpaStaticFiles();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseReactDevelopmentServer(npmScript: "start");
            }
        });
    }
}
  • 主应用程序的 URL 是: http://vmsaudit.esdev2.elwood.local
  • 服务器的 API 调用 URL 为: http://vmsaudit.esdev2.elwood.local/api/Company/er

这是我的公司控制器的代码:

[ApiController]
[Route("api/[controller]")]
public class CompanyController
{
    VMSImportEngineContext db;
    ErecruitContext erdb;
    IConfiguration config;

    public CompanyController (VMSImportEngineContext db, ErecruitContext erdb, IConfiguration config)
    {
        this.db = db;
        this.erdb = erdb;
        this.config = config;
    }

    [HttpGet("{datastore}")]
    public List<Company> Get(string datastore)
    {
        List<Company> ret = new List<Company>();
        if ("ss".Equals(datastore))
        {
            List<int?> vmsClients = db.VmsClient
                .Where(c => c.legacyID != null && c.audit == true)
                .Select(c => c.legacyID)
                .ToList();

            ret = this.GetSSCompanies(vmsClients);
        }
        else // Has to be ERecruit client
        {
            List<int> vmsClients = db.VmsClient
                .Where(c => c.audit == true)
                .Select(c => c.clientid)
                .ToList();

            ret = this.erdb
                .Company
                .Where(c => vmsClients.Contains(c.company_id))
                .ToList();
        }

        return ret; 
    }

    private List<Company> GetSSCompanies(List<int?> vmsClients)
    {
        List<Company> ret = new List<Company>();
        // Generate a linked query
        string connectionString = this.config.GetConnectionString("vmsimportengine");
        SqlConnection connection = new SqlConnection(connectionString);
        if (connection.State != System.Data.ConnectionState.Open)
        {
            connection.Open();
        }
        DbCommand command = connection.CreateCommand();
        command.CommandText = GenSSCompanyQuery(vmsClients);
        DbDataReader reader = command.ExecuteReader();
        while (reader.Read())
        {
            Company company = GetCompanyFromDataRow(reader);
            ret.Add(company);
        }
        return ret;
    }


    private Company GetCompanyFromDataRow(DbDataReader reader)
    {
        Company c = new Company();
        if (!reader.IsDBNull(reader.GetOrdinal("name")))
        {
            c.name = reader.GetString(reader.GetOrdinal("name"));
        }
        if (!reader.IsDBNull(reader.GetOrdinal("company_component_id")))
        {
            int cid = (int)reader.GetDouble(reader.GetOrdinal("company_component_id"));
            // now get the real ID from the current system...
            List<int> potential = this.db.VmsClient
                .Where(c => c.legacyID == cid)
                .Select(c => c.clientid)
                .ToList();
            if (potential.Count > 0)
            {
                c.company_id = potential[0];
            }
        }
        return c;
    }

    private string GenSSCompanyQuery(List<int?> vmsClients)
    {
        // Code Suppressed
    }
}

更新

我决定向我的控制器添加一个新方法,该方法似乎正在返回数据......我的问题显然是在 API URL 调用的末尾传递一个参数???这是我刚刚添加到我的控制器中成功返回“pong”的方法

    [HttpGet]
    public string ping()
    {
        return "pong";
    }

总之:

唯一的区别是 URL 末尾的参数

2个回答

您的代码中有几个潜在的问题。没有人会确切地告诉您出了什么问题,但您可以尝试使用一些设置。

  1. 尝试在您的 startup.cs 中注释此行。更好的是,在 IIS 端使用 HTTPS 重定向。您的应用程序正在运行在 IIS 后面的 Kestrel 服务器上运行,因此如果未在 IIS 上设置 SSL,您的应用程序将因一些神秘错误而静默死亡,例如无法访问站点
app.UseHttpsRedirection();
  1. 在生产中,你应该自己构建 React 项目,也许你忘了这样做。出于测试目的,请尝试注释以下所有行或将 appsettings.json 中的环境设置为 DEVELOPMENT。在这种情况下,您可以尝试检查您的应用程序在生产中的运行方式是否与在本地计算机上的运行方式相同。
env.IsDevelopment()
  1. 为避免 CORS 问题,请尝试不仅允许所有主机,还允许所有标头和方法
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()

https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1

  1. 默认情况下,Kestrel 服务器只接受来自本地主机的请求。当您将其部署到生产服务器时,您可能需要允许来自特定 IP 或特定端口上的所有 IP 的请求。
"Kestrel": {
  "EndPoints": {
    "Http": {
      "Url": "http://0.0.0.0:5000"
    }
  }
}

有关如何修复 IIS 其他潜在问题的详细指南,请查看此答案。

https://superuser.com/questions/1514511/iis-is-not-accessible-remotely-on-vps-hosting

问题是数据库连接。我使用的应用程序池的身份验证凭据不正确。结果是所有使用连接的 REST 服务调用都返回了 index.html 页面。我认为这是因为实体框架代码在启动时抛出了异常并且从未到达路由代码?