[英]How return View with model in the ExceptionFilter in Asp.Net Core MVC
我创建了一个 Asp.Net Core MVC 应用程序。 我想处理两种类型的错误。
我创建了两个异常: UserFriendlyException
和UserFriendlyViewException
。
我已尝试根据以下规则创建我需要处理这两个异常的ExceptionFilter
:
如果调用了UserFriendlyViewException
异常,那么我想返回带有原始 ViewName 和AddModelError
的 ViewResult 并返回原始 Model。
如果调用了UserFriendlyException
异常,那么我想重定向到Error
视图。
这是我的ExceptionFilterAttribute
:
public class ControllerExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly ITempDataDictionaryFactory _tempDataDictionaryFactory;
private readonly IModelMetadataProvider _modelMetadataProvider;
public ControllerExceptionFilterAttribute(ITempDataDictionaryFactory tempDataDictionaryFactory,
IModelMetadataProvider modelMetadataProvider)
{
_tempDataDictionaryFactory = tempDataDictionaryFactory;
_modelMetadataProvider = modelMetadataProvider;
}
public override void OnException(ExceptionContext context)
{
if (!(context.Exception is UserFriendlyException) && !(context.Exception is UserFriendlyViewException)) return;
var tempData = _tempDataDictionaryFactory.GetTempData(context.HttpContext);
//CreateNotification(NotificationHelpers.AlertType.Error, tempData, context.Exception.Message);
if (!tempData.ContainsKey(NotificationHelpers.NotificationKey)) return;
if (context.Exception is UserFriendlyViewException userFriendlyViewException)
{
context.ModelState.AddModelError(userFriendlyViewException.ErrorKey, userFriendlyViewException.Message);
}
if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
//How pass here Model from context??
//If exists more views with same name but in another controller how pass correct ViewName?
var result = new ViewResult
{
ViewName = context.Exception is UserFriendlyViewException ?
controllerActionDescriptor.ActionName
: "Error",
TempData = tempData,
ViewData = new ViewDataDictionary(_modelMetadataProvider, context.ModelState)
{
{"Notifications", tempData[NotificationHelpers.NotificationKey] },
}
};
context.ExceptionHandled = true;
context.Result = result;
}
tempData.Remove(NotificationHelpers.NotificationKey);
}
}
我有两个问题:
1.) 如何将原始Model
从ExceptionContext
传递到ViewResult
?
2.) 如果存在更多具有相同名称但在另一个 Controller 中的视图,如何为UserFriendlyViewException
返回正确的 ViewName?
如何将原始模型从 ExceptionContext 传递给 ViewResult?
您可以使用context.ModelState
集合。
foreach(var item in context.ModelState)
{
string parameter = item.Key;
object rawValue = item.Value.RawValue;
string attemptedValue = item.Value.AttemptedValue;
System.Console.WriteLine($"Parameter: {parameter}, value: {attemptedValue}");
}
请注意,集合将仅包含绑定参数。
如果存在更多同名但在另一个控制器中的视图,如何为 UserFriendlyViewException 返回正确的 ViewName?
框架将使用与控制器操作中相同的视图发现过程,因此您可以指定路径而不是视图名称:
可以提供视图文件路径而不是视图名称。 如果使用从应用根目录开始的绝对路径(可以选择以“/”或“~/”开头),则必须指定 .cshtml 扩展名:
return View("Views/Home/About.cshtml");
您还可以使用相对路径在不带 .cshtml 扩展名的情况下指定不同目录中的视图。 在 HomeController 中,您可以使用相对路径返回 Manage 视图的 Index 视图:
return View("../Manage/Index");
同样,您可以使用“./”前缀指示当前特定于控制器的目录:
return View("./About");
通过 ViewData 传递东西
public class UIFriendlyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.Exception is UIFriendlyException uex)
{
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), context.ModelState);
//here we go
viewData.Add("Message", MY_CUSTOM_MESSAGE);
var res = new ViewResult() {
ViewName = "Error",
ViewData = viewData
};
context.Result = res;
}
context.ExceptionHandled = true;
}
}
然后在 Startup 中注册
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(UIFriendlyException.UIFriendlyExceptionFilter));
});
准备两个过滤器。 一个缓存绑定模型,另一个处理异常。
动作过滤器
public class BindModelCacheFilter : IActionFilter
{
public static readonly string Key = "BindModelCacheFilterKey. This string can be anything.";
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Items[BindModelCacheFilter.Key] = context.ActionArguments;
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
异常过滤器
public class ExceptionHandlingFilter : IExceptionFilter
{
private readonly ILogger<ExceptionHandlingFilter> _logger;
public ExceptionHandlingFilter(ILogger<ExceptionHandlingFilter> logger)
{
_logger = logger;
}
public void OnException(ExceptionContext context)
{
// ...
var requestPayload = "";
try
{
var actionArguments = context.HttpContext.Items[BindModelCacheFilter.Key] as IDictionary<string, object?>;
if (0 < actionArguments?.Count)
{
object requestDto = actionArguments.First().Value!;
var options = new System.Text.Json.JsonSerializerOptions()
{
IncludeFields = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
};
requestPayload = System.Text.Json.JsonSerializer.Serialize(requestDto, options);
}
}
catch
{
}
// ...
}
}
创建一个从 IExceptionFilter 和 IActionFilter 派生的 ExceptionFilter
这使您可以访问视图和 controller
[TypeFilter(typeof(ExceptionFilter))]
public class MyController : ControllerBase
{
public IActionResult SomeAction([Bind("ID")] MyView vm) {}
}
public class ExceptionFilter : IExceptionFilter, IActionFilter
{
private MyController? MyController { get; set; }
private MyViewModel vm { get; set; }
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
MyController = (MyController)context.Controller;
//
if (context.ActionArguments.TryGetValue("vm", out object vmObj))
vm = vmObj as MyView;
else
vm = null;
}
public void OnException(ExceptionContext context)
{
// Do something using MyController and MyView
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.