【问题标题】:Response Redirect Using In MVC在 MVC 中使用响应重定向
【发布时间】:2016-08-22 11:48:03
【问题描述】:

我想在 global.asax 和我的控制器之间建立连接。我们在我们的网站上使用了一个数据库。我们有一个代码来检查数据库是否存在。如果它不存在,我们想显示一条消息,如“数据库初始化”等。

GLOBAL.ASAX中的数据库检查:

public class MvcApplication : System.Web.HttpApplication
{
    public static bool flag;
    protected void Application_Start()
    {
        Database.SetInitializer<PhoneDexContext>(null);

        var connString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;

        using (PhoneDexContext db = new PhoneDexContext())
        {
            if (!db.Database.Exists())
            {
                flag = true;
                db.Database.CreateIfNotExists();              
            }

        }

        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    protected void Application_BeginRequest()
    {
        if (flag)
        {
            Response.Redirect("Loading/LoadingScreen");
        }
    }
}

但是我们在使用响应重定向时遇到了问题。它返回

{“响应在此上下文中不可用。”}.

我们有一个控制器,它是 LoadingController,它有这个代码;

public class LoadingController : Controller
{
    // GET: Loading
    public ActionResult LoadingScreen()
    {

        return View();
    }
}

但是我们不能跳到这部分。我怎样才能建立连接?谢谢

【问题讨论】:

  • 你在哪里执行这个数据库检查代码? MVC 请求管道的哪一部分?
  • 在 global.asax 中。我们不知道请求管道
  • 请求和响应在启动时不可用。创建一个动作过滤器并让它进行数据库检查,然后根据需要重定向
  • 您可以将 Session 值设置为 false,然后在 Home 控制器上检查,默认方法
  • 嗯,但如果数据库未准备好,我想向用户显示警报,等待加载。我该怎么做?我想我应该在全球 asax 做

标签: c# asp.net-mvc asp.net-mvc-4


【解决方案1】:

首先,Application_Start 不处理任何用户请求。它只是执行一些启动初始化。它仅在应用启动时调用一次。要根据用户的操作进行一些检查并正确响应,您需要将这些检查移至 Application_BeginRequest 方法中。

其次,您还需要在响应重定向到该页面之前检查用户是否已经请求/Loading/LoadScreen。否则,在创建数据库之前,您将获得无限重定向。

public class MvcApplication : HttpApplication
{
    private static bool dbInitialized;

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        // We can do it asynchronously to not block other initialization code.
        Task.Run((Action)CreateDataBase);
    }

    private static void CreateDataBase()
    {
        Database.SetInitializer<PhoneDexContext>(null);

        using (PhoneDexContext db = new PhoneDexContext())
        {
            if (!db.Database.Exists())
                db.Database.CreateIfNotExists();
        }

        dbInitialized = true;
    }

    protected void Application_BeginRequest()
    {
        if (!dbInitialized && !this.Request.Url.LocalPath.StartsWith("/Loading/LoadingScreen", StringComparison.OrdinalIgnoreCase))
        {
            this.Response.Redirect("/Loading/LoadingScreen");
        }
    }
}

您可以进一步检查并将检查移至ActionFilter,因为您将能够使用RouteData 并检查actioncontroller 参数而不是url。与nameof 结合使用,路由更改、重命名、重构等更不容易出错。

【讨论】:

    【解决方案2】:

    我认为这个答案会给你你想要的:Return different views in a controller

    在你的情况下,而不是 response.redirect 使用 return View("/Loading/LoadScreen"); ...或类似的东西

    【讨论】:

    • global.asax 返回无效。我无法返回视图。我必须去一个控制器,它去查看
    • 也许你可以创建一个特定的加载路径,然后使用:Response.RedirectToRoute("Loading");
    【解决方案3】:

    试试这个

    HttpContext.Current.Response.Redirect("/Loading/LoadScreen");
    

    因为 Response 对象在 global.asax 的 application_start 方法中不可用。 您必须在任何情况下使用 HttpContext.Current。

    【讨论】:

    • 我应该移动 createIfNotExist 部分来开始请求吗?你能写代码吗?我将编辑我的第一个代码,请再次检查
    • 你能不能写 db.Database.CreateIfNotExists();在 HttpContext.Current.Response.Redirect("/Loading/LoadScreen");它会正常工作的。
    【解决方案4】:

    您还可以使用“自动启动功能”(How to warm up an ASP.NET MVC application on IIS 7.5?:达林斯回答)。这将在网站准备好服务请求之前执行一次。缺点是您无法向用户显示“请稍候”窗口。就我个人而言,我不会在每次请求时检查数据库是否存在。只需执行它;我猜数据库更新应该不会花很长时间吧?

    【讨论】:

      【解决方案5】:

      Application_Start 发生在 ASP.Net 开始处理请求之前。

      您可以设置一个全局静态标志来指示错误情况,然后处理Application_BeginRequest并检查该标志并重定向。

      static bool _isDbLoaded;
      public class MvcApplication : System.Web.HttpApplication
      { 
          protected void Application_Start(){
              Database.SetInitializer<PhoneDexContext>(null);
      
              var connString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;
      
              using (PhoneDexContext db = new PhoneDexContext())
              {
                  if (!db.Database.Exists())
                  {
                      _isDbLoaded = false;
                      db.Database.CreateIfNotExists();
                   }
               }
              AreaRegistration.RegisterAllAreas();
              FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
              RouteConfig.RegisterRoutes(RouteTable.Routes);
              BundleConfig.RegisterBundles(BundleTable.Bundles);
         }
      }
      
      protected void Application_BeginRequest(){
          if(!_isDbLoaded){
              Response.Redirect("Loading/LoadingPage");
          }
      }
      

      【讨论】:

      • 我猜它进入了无限循环:(。它是一个循环,但我不知道它是否无限
      • 我会尽快编辑。应该检查当前页面是否是重定向目的地
      • 因为_isDbLoaded 仅在启动时设置,以后永远不会更改应用程序将始终重定向到LandingPage。此外,您不检查用户是否已经在登陆页面上,因此即使从登陆页面也会将用户重定向到登陆。
      • @lorond 好点。他需要在 CreateIfNotExists 之后将该变量设置为 true。问题是,他永远不会重定向到那个页面。它只会等待创建,除非它是异步创建
      • 它的无限循环:(对不起
      【解决方案6】:

      由于RequestResponse 在您的Application_Start 事件中不可用,您可能会考虑将此代码放在MVC 请求-响应管道中的某个位置。动作过滤器是个好地方。

      public class VerifySetupIsGood : ActionFilterAttribute
      {
          public override void OnActionExecuting(ActionExecutingContext context)
          {
              var dbSetDate = context.HttpContext.Application["DbSetDate"] as string;
              if (String.IsNullOrEmpty(dbSetDate))
              {
                 //to do : Execute your custom code to check db existence here
      
                  var values = new Dictionary<string, string> { { "action", "LoadScreen" },
                                                                   { "controller", "Loading" } };
      
                  var r = new RouteValueDictionary(values);
                  //redirect the request to MissingDatabase action method.
                  context.Result = new RedirectToRouteResult(r);
              }            
              base.OnActionExecuting(context);
          }
      }
      

      这里我们首先检查应用程序变量是否具有“DbSetDate”键的有效条目。默认情况下,它不会存在。然后你必须执行你的自定义代码来检查你的数据库是否存在。如果没有,请重定向到 LoadScreen 操作。

      在全局范围内注册此过滤器,以便对到达您的应用程序的任何请求执行该过滤器。

      GlobalFilters.Filters.Add(new VerifySetupIsGood());
      

      现在,当您完成数据库设置后,请更新此应用程序变量以使其具有有效值。

      HttpContext.Application["DbSetDate"] = DateTime.Now.ToString();
      

      请记住,应用程序变量状态也会被重置。所以不要仅仅依赖它。您应该运行您的自定义代码来检查您的数据库是否存在于 if 条件中。如果条件是阻止您的自定义数据库检查每个请求。

      【讨论】:

      • 我不会为每个请求(可能会发生很多次)执行一个操作,因为它只是偶尔发生(数据库更新)
      • 它检查的第一件事是应用程序变量值。所以if条件里面的代码不会对所有的请求都执行。
      • 很公平,但过滤器仍然会在每个请求中执行。我的观点是,这并不能反映在应用程序启动时检查数据库是否存在的要求。
      • 不要忘记检查用户是否已经在LoadScreen。否则它将无限重定向,因为过滤器是全局的。
      猜你喜欢
      • 1970-01-01
      • 2014-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-29
      • 2019-09-20
      相关资源
      最近更新 更多