Как вернуть HttpError ConfigurationElement обратно в Inherited с помощью IIS 7 API

Предположим, у меня есть следующая коллекция <httpErrors> в web.config:

<httpErrors>
</httpErrors>

Да, красиво и пусто.

А в IIS 7 моя страница с ошибками HTTP выглядит так:

httpErrors

Красивый! (Я выделил 404 просто потому, что это пример, который я буду использовать через секунду).

Теперь я запускаю следующий код:

errorElement["statusCode"] = 404;
errorElement["subStatusCode"] = -1;
errorElement["path"] = "/404.html";
httpErrorsCollection.Add(errorElement);

Прекрасный. Теперь у меня есть, как и ожидалось, это в моем web.config:

<httpErrors>
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" subStatusCode="-1" prefixLanguageFilePath="" path="/404.html" />
</httpErrors>

Не может быть счастливее. Теперь в IIS 7 мой раздел ошибок HTTP выглядит, как и ожидалось, как показано ниже: alt text

Жизнь не может быть слаще в этот момент. Теперь, в будущем, я хочу программно вернуть мою ошибку 404 обратно в состояние, показанное на исходном снимке экрана. Логика подсказывает, что я должен remove свою новую ошибку:

httpErrorsCollection.Remove(errorElement);

Но увы, если я сделаю так, мой web.config будет выглядеть примерно так:

    <httpErrors>
        <remove statusCode="404" subStatusCode="-1" />
    </httpErrors>

И мой IIS выглядит примерно так:

альтернативный текст

Это ожидается из-за моего web.config, но как, используя ServerManager и весь его полезный API IIS 7, полностью удалить элемент httpError и вернуть мой web.config обратно к:

<httpErrors>
</httpErrors>

Любые идеи?


person joshcomley    schedule 23.11.2010    source источник


Ответы (3)


В IIS7 и более поздних версиях в разделе Управление мы можем увидеть значок с именем Редактор конфигурации, двойной щелчок и развернуть, чтобы

system.webserver-->webdav-->httpErrors

Нажмите правой кнопкой мыши на Путь по умолчанию в разделе

раздел --> Нажмите Вернуться к родительскому

Затем перезапустите веб-сайт.

Изменения будут возвращены обратно

person sushma    schedule 17.12.2012
comment
Это не отвечает на вопрос - в OP конкретно упоминаются ServerManager и API IIS 7, который представляет собой решение .Net, предназначенное для помощи разработчикам в управлении своими серверами IIS с помощью кода. Этот ответ специально предназначен для управления графическим интерфейсом сервера. - person Richard Barker; 14.06.2016

Я сталкивался с этим раньше. Единственный способ, которым я мог заставить это работать, состоял в том, чтобы вызвать RevertToParent() в разделе system.webServer/httpErrors и зафиксировать изменения, прежде чем вносить какие-либо изменения, например:

ConfigurationSection httpErrorsSection = 
           config.GetSection("system.webServer/httpErrors");

// Save a copy of the errors collection first (see pastebin example)
 httpErrorsCollectionLocal = 
            CopyLocalErrorCollection(httpErrorsSection.GetCollection()
            .Where(e => e.IsLocallyStored)
            .ToList<ConfigurationElement>());

httpErrorsSection.RevertToParent();
serverManager.CommitChanges();

Вы должны зафиксировать после вызова RevertToParent() is called because the errors collection remains intact untilCommitChanges()`.

Затем вам нужно повторно добавить сохраненную копию коллекции локальных ошибок, удалив или обновив пользовательские ошибки по мере их повторного добавления.

Я вставил полный рабочий пример ниже. Код работает в корне сайта web.config, но немного поработав, вы можете добавить поддержку файлов web.config во вложенных папках:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Web.Administration;

namespace IIS7_HttpErrorSectionClearing
{
  class Program
  {
    static void Main(string[] args)
    {
      long siteId = 60001;

      // Add some local custom errors
      CustomErrorManager.SetCustomError(siteId, 404, -1, ResponseMode.File, @"D:\websites\60001\www\404.html");
      CustomErrorManager.SetCustomError(siteId, 404, 1, ResponseMode.File, @"D:\websites\60001\www\404-1.html");
      CustomErrorManager.SetCustomError(siteId, 404, 5, ResponseMode.File, @"D:\websites\60001\www\404-5.html");

      // Change existing local custom error
      CustomErrorManager.SetCustomError(siteId, 404, 1, ResponseMode.ExecuteURL, "/404-5.aspx");

      // Revert to inherited
      CustomErrorManager.RevertCustomError(siteId, 404, 5);
      CustomErrorManager.RevertCustomError(siteId, 404, -1);
      CustomErrorManager.RevertCustomError(siteId, 404, 1);
    }
  }

  public enum ResponseMode
  {
    File,
    ExecuteURL,
    Redirect
  }  

  public static class CustomErrorManager
  {
    public static void RevertCustomError(long siteId, int statusCode, int subStatusCode)
    {
      List<Error> httpErrorsCollectionLocal = CopyLocalsAndRevertToInherited(siteId);
      int index = httpErrorsCollectionLocal.FindIndex(e => e.StatusCode == statusCode && e.SubStatusCode == subStatusCode);
      if(index > -1)
      {
        httpErrorsCollectionLocal.RemoveAt(index);
      }
      PersistLocalCustomErrors(siteId, httpErrorsCollectionLocal);
    }

    public static void SetCustomError(long siteId, long statusCode, int subStatusCode, 
                                          ResponseMode responseMode, string path)
    {
      SetCustomError(siteId, statusCode, subStatusCode, responseMode, path, null);
    }

    public static void SetCustomError(long siteId, long statusCode, int subStatusCode, 
                                          ResponseMode responseMode, string path, string prefixLanguageFilePath)
    {
      List<Error> httpErrorsCollectionLocal = CopyLocalsAndRevertToInherited(siteId);
      AddOrUpdateError(httpErrorsCollectionLocal, statusCode, subStatusCode, responseMode, path, prefixLanguageFilePath);
      PersistLocalCustomErrors(siteId, httpErrorsCollectionLocal);
    }

    private static void PersistLocalCustomErrors(long siteId, List<Error> httpErrorsCollectionLocal)
    {
      using (ServerManager serverManager = new ServerManager())
      {
        Site site = serverManager.Sites.Where(s => s.Id == siteId).FirstOrDefault();
        Configuration config = serverManager.GetWebConfiguration(site.Name);
        ConfigurationSection httpErrorsSection = config.GetSection("system.webServer/httpErrors");
        ConfigurationElementCollection httpErrorsCollection = httpErrorsSection.GetCollection();

        foreach (var localError in httpErrorsCollectionLocal)
        {
          ConfigurationElement remove = httpErrorsCollection.CreateElement("remove");
          remove["statusCode"] = localError.StatusCode;
          remove["subStatusCode"] = localError.SubStatusCode;
          httpErrorsCollection.Add(remove);

          ConfigurationElement add = httpErrorsCollection.CreateElement("error");
          add["statusCode"] = localError.StatusCode;
          add["subStatusCode"] = localError.SubStatusCode;
          add["responseMode"] = localError.ResponseMode;
          add["path"] = localError.Path;
          add["prefixLanguageFilePath"] = localError.prefixLanguageFilePath;
          httpErrorsCollection.Add(add);
        }
        serverManager.CommitChanges();
      }
    }

    private static List<Error> CopyLocalsAndRevertToInherited(long siteId)
    {
      List<Error> httpErrorsCollectionLocal;
      using (ServerManager serverManager = new ServerManager())
      {
        Site site = serverManager.Sites.Where(s => s.Id == siteId).FirstOrDefault();
        Configuration config = serverManager.GetWebConfiguration(site.Name);
        ConfigurationSection httpErrorsSection = config.GetSection("system.webServer/httpErrors");
        ConfigurationElementCollection httpErrorsCollection = httpErrorsSection.GetCollection();

        // Take a copy because existing elements can't be modified.
        httpErrorsCollectionLocal = CopyLocalErrorCollection(httpErrorsSection.GetCollection()
                                      .Where(e => e.IsLocallyStored).ToList<ConfigurationElement>());

        httpErrorsSection.RevertToParent();

        // Have to commit here because RevertToParent won't clear the collection until called.
        serverManager.CommitChanges();
        return httpErrorsCollectionLocal;
      }
    }

    private static List<Error> CopyLocalErrorCollection(List<ConfigurationElement> collection)
    {
      List<Error> errors = new List<Error>();
      foreach (var error in collection)
      {
        errors.Add(new Error()
        {
          StatusCode = (long)error["statusCode"],
          SubStatusCode = (int)error["subStatusCode"],
          ResponseMode = (ResponseMode)error["responseMode"],
          Path = (string)error["path"],
          prefixLanguageFilePath = (string)error["prefixLanguageFilePath"]
        });
      }
      return errors;
    }

    private static void AddOrUpdateError(List<Error> collection, long statusCode, int subStatusCode, 
                                             ResponseMode responseMode, string path, string prefixLanguageFilePath)
    {
      // Add or update error
      Error error = collection.Find(ce => ce.StatusCode == statusCode && ce.SubStatusCode == subStatusCode);
      if (error == null)
      {
        collection.Add(new Error()
        {
          StatusCode = statusCode,
          SubStatusCode = subStatusCode,
          ResponseMode = responseMode,
          Path = path,
          prefixLanguageFilePath = prefixLanguageFilePath
        });
      }
      else
      {
        error.ResponseMode = responseMode;
        error.Path = path;
        error.prefixLanguageFilePath = prefixLanguageFilePath;
      }
    }

    private class Error
    {
      public long StatusCode { get; set; }
      public int SubStatusCode { get; set; }
      public ResponseMode ResponseMode { get; set; }
      public string Path { get; set; }
      public string prefixLanguageFilePath { get; set; }
    }  
  }
}
person Kev    schedule 24.11.2010
comment
Это единственный способ, который я тоже смог найти :( Я создал свой собственный метод, чтобы сделать это, используя метод RevertToParent(), который также сохраняет любые другие изменения, но это ужасно. Я скоро опубликую его. - person joshcomley; 26.11.2010

Ну вот что я нашел по этому вопросу:

ServerManager serverManager = new ServerManager();
Configuration config = serverManager.GetWebConfiguration(siteName);
ConfigurationSection httpErrorsSection = config.GetSection("system.webServer/httpErrors");
ConfigurationElementCollection httpErrorsCollection = httpErrorsSection.GetCollection();
foreach (ConfigurationElement errorEle in httpErrorsCollection) {
    if (errorEle("statusCode") == statusCode && errorEle("subStatusCode") == subStatusCode) {
        errorEle.Delete();
        serverManager.CommitChanges();
        return;
    }
}

Я получаю список элементов как обычно, затем циклически нахожу тот, который мне нужен, а затем вызываю удаление элемента. Вот так и работает. Я не знаю, существовало ли это, когда был задан этот вопрос, но это происходит сейчас. Вам нужно убедиться, что вы знаете этот код состояния и код подстатуса.

person Richard Barker    schedule 13.06.2016