在控制器和ActionFilter之外获取ModelState

时间:2019-01-02 14:27:15

标签: c# asp.net-core httpclientfactory

是否可以通过DI(在Controller或ActionFilter之外)访问ModelStateDictionary?我想我可以制作一个ActionFilter来存储对ModelStateDictionary的引用,以便以后可以在其他地方访问它,但是我想知道是否有常规的访问方式,例如IHttpContextAccessorHttpContext

我们有一个网络应用程序,它是api的客户端。之所以这样做,是因为我想从API客户端(类型为http客户端)使用的ModelState向Web应用程序的DelegatingHandler自动添加错误。处理程序将监视来自API的每个响应,并为适用的响应(主体中包含400个带有自定义错误代码的响应,例如“名称已被接受”)向ModelState添加一条消息。

到目前为止,我已经尝试过请求ControllerContext,但它似乎始终为空。

var controllerContext = _serviceProvider.GetService<ControllerContext>();
controllerContext?.ModelState.AddModelError("", result.ErrorMessage);

我还使用VS调试器查看了所有已注册的服务,但是找不到任何有希望的东西。


关于SRP的评论和关注点分离的

附带说明(很大):我认为这不违反SRP。 API客户端是我们API的通用客户端实现,可以在任何地方使用(我们目前在Xamarin和ASP.NET Core MVC Web应用程序上使用它-本问题中提到的一个)。但是,API客户端期望在其构造函数中使用HttpClient,这意味着其行为可以被其使用者修改。

例如,Web应用程序使用DI提供API客户端所需的HttpClientHttpClient设置为使用两个委托处理程序,其中一个是我在此问题中描述的处理程序。

关于ModelState是否应在控制器之外进行操作:好吧,这正是FluentValidation之类的库(和ASP.NET默认验证)所做的。

关于是否应该在ModelState中操纵DelegatingHandler:我认为这是一个更有效的讨论。但是,没有人真的没有提出任何关于为什么这样做不好的论据。

无论是否要执行此操作,都应该“自动”完成:我认为将代码放在一个地方比在每次对api的每次调用中都要记住每次执行操作要好得多。

关于是否应将这些消息甚至放到ModelState中:好吧,如果我在这里提到,该注释将变得太大。而且,没有人真的对此争论过,所以...

2 个答案:

答案 0 :(得分:2)

您无法获得ControllerContext本身,但可以得到的是ActionContext(这是控制器上下文的更专业版本)。您可以使用IActionContextAccessor

var actionContextAccessor = _serviceProvider.GetService<IActionContextAccessor>();
var actionContext = actionContextAccessor.ActionContext;
actionContext?.ModelState.AddModelError("", result.ErrorMessage);

实际上,您不需要每次都解析操作上下文访问器,但是可以保留它的一个实例,并在需要时访问操作上下文。 ActionContext将在您处理请求时处于动作范围内时设置。

另一种解决方案是将关注点分离并将错误存储在其他位置,然后在操作过滤器中从那里填充模型状态。这样,您的解决方案将不仅限于MVC,而且还可以在其他位置访问这些错误。

答案 1 :(得分:0)

在我看来,这样做会打破单一责任原则,因为http客户端对控制器一无所知。
但是,如果您确实要这样做,则可以使用lambda表达式将ModelStateDictionary的引用传递给您的委托,请参见:Passing delegate function with extra parameters


如果不想破坏SRP,一种选择是使http客户端返回错误列表,该错误列表将由控制器添加到模型状态(如果没有错误,则为null)