Как использовать HandleError с ошибками состояния модели

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

Например, возьмите мой предыдущий код:

public ActionResult SomeAction(SomeViewModel model)
{
    try
    {
        _someService.SomeMethod(model);
    }
    catch (ServiceException ex)
    {
        ModelState.AddModelError(ex.Key, ex.ErrorMessage);
    }

    return View();
}

По сути, он вызовет службу, и если будет выдано исключение ServiceException, он узнает, что возникла проблема с данными модели, и добавит ошибку в ModelState, а затем просто вернет представление. Но я заметил несколько паттернов, очень не похожих на DRY, потому что у меня был один и тот же код try/catch в каждом методе действия.

Итак, чтобы немного высушить это, я в основном создал новый фильтр действий HandleServiceError:

public class HandleServiceErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        ((Controller)context.Controller)
            .ModelState
            .AddModelError(
                ((ServiceException)context.Exception).Key, 
                ((ServiceException)context.Exception).ErrorMessage
            );
        context.ExceptionHandled = true;
    }
}

Затем упростил мои методы действий следующим образом:

public ActionResult SomeAction(SomeViewModel model)
{
    _someService.SomeMethod(model);
    return View();
}

Проблема в том, что как только фильтр действий обрабатывает ошибку, он не возвращается к моему методу действия. Я как бы понимаю, под капотом, почему это происходит. Но я все равно хотел бы найти способ сделать то, что я пытаюсь сделать.

Это возможно?

Заранее спасибо.

ОБНОВЛЕНИЕ:

Я попробовал предложения из статьи, предоставленной Дарином. в своем ответе, но столкнулся с проблемами при попытке использовать инъекцию конструктора с состоянием модели контроллера.

Например, если вы посмотрите на их код Controllers\ProductController.cs, у них есть пустой конструктор контроллера, использующий локатор службы для создания службы, передавая в этот момент ModelState контроллера:

public ProductController() 
{
    _service = new ProductService(new ModelStateWrapper(this.ModelState), 
                                  new ProductRepository());
}

Но если вы посмотрите на введенный конструктор, предполагается, что ModelState будет внедрен в конструктор службы:

public ProductController(IProductService service)
{
    _service = service;
}

Я не знаю, как заставить CI работать с ModelState текущего контроллера. Если бы я мог понять это, тогда этот подход мог бы работать.


person Jerad Rose    schedule 28.03.2011    source источник


Ответы (1)


Вы все еще можете вернуть соответствующее представление:

context.Result = new ViewResult
{
    ViewName = context.RouteData.GetRequiredString("action")
};

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

person Darin Dimitrov    schedule 28.03.2011
comment
Спасибо. Я попытался пойти по пути использования ModelStateWrapper в этой статье, и это оказалось проблемой с Unity IoC. Я не могу понять, как внедрить мой Controller.ModelState в мой сервисный слой. Если я перейду от внедрения конструктора к локаторам служб, возможно, я смогу это сделать, но я не хочу переходить на локаторы служб только для этого. Я посмотрел и не нашел никаких элегантных решений для этого (большинство из них кажутся взломанными или слишком сложными). - person Jerad Rose; 28.03.2011
comment
Я обновил свой пост с дополнительной информацией по этому поводу. Любопытно узнать, есть ли способ заставить это работать с помощью внедрения конструктора. - person Jerad Rose; 23.06.2011
comment
Метод, описанный в статье, требует, чтобы вы вручную проверяли правильность каждого метода службы, и не дает никакой конкретной информации об ошибке, просто проходит или не проходит. Кроме того, если вы вызываете несколько сервисов в действии или если один сервис вызывает другой, вам нужно проверять истинность на всем пути вверх по стеку. Оригинальный метод Джерада, хотя и требует попытки поймать, более элегантен, ИМО. - person Josh Noe; 25.01.2013