【问题标题】:Async using ModelState with RedirectToAction in ASP.NET Core 1.0在 ASP.NET Core 1.0 中使用带有 RedirectToAction 的 ModelState 进行异步
【发布时间】:2016-12-31 05:42:29
【问题描述】:

我有一个删除对象的方法。删除不属于自己的视图,是“EditReport”中的“Delete”按钮。成功移除“报告”上的重定向后。

[HttpPost]
[Route("{reportId:int}")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteReport(int reportId)
{
    var success = await _reportService.DeleteReportControlAsync(reportId);
    if (success == false)
    {
        ModelState.AddModelError("Error", "Messages");
        return RedirectToAction("EditReport");
    }
    ModelState.AddModelError("OK", "Messages");
    return RedirectToAction("Report");
}

在 ASP.NET Core 1.0(完整的 .NET Framework)中,我使用以下属性在方法之间保存 ModelState。我从这里拿的:https://stackoverflow.com/a/35987804/3878213

我最近转而对控制器、服务和存储库使用异步方法。 在我看来,动作的属性也应该是异步的。 最初,我将代码属性改写如下:

public class SetTempDataModelStateAttribute : Attribute, IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext filterContext, ActionExecutionDelegate next)
        {    
            var controller = filterContext.Controller as Controller;
            var modelState = controller?.ViewData.ModelState;
            if (modelState != null)
            {
                var listError = modelState.ToDictionary(m => m.Key, m => m.Value.Errors
                    .Select(s => s.ErrorMessage)
                    .FirstOrDefault(s => s != null));
                var listErrorJson = await Task.Run(() => JsonConvert.SerializeObject(listError));
                controller.TempData["ModelState"] = listErrorJson;
            }
            await next();
        }
    }
public class RestoreModelStateFromTempDataAttribute : Attribute, IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext filterContext, ActionExecutionDelegate next)
    {
        var controller = filterContext.Controller as Controller;
        var tempData = controller?.TempData?.Keys;
        if (controller != null && tempData != null)
        {
            if (tempData.Contains("ModelState"))
            {
                var modelStateString = controller.TempData["ModelState"].ToString();
                var listError = await Task.Run(() => 
                    JsonConvert.DeserializeObject<Dictionary<string, string>>(modelStateString));
                var modelState = new ModelStateDictionary();
                foreach (var item in listError)
                {
                    modelState.AddModelError(item.Key, item.Value ?? "");
                }

                controller.ViewData.ModelState.Merge(modelState);
            }
        }
        await next();
    }
}

对我来说这似乎是合乎逻辑的。但是这段代码并没有像我预期的那样工作。 但以下异步属性工作正常:

public class SetTempDataModelStateAttribute : ActionFilterAttribute
    {
        public override async Task OnActionExecutionAsync(ActionExecutingContext filterContext, ActionExecutionDelegate next)
        {
            await base.OnActionExecutionAsync(filterContext, next);

            var controller = filterContext.Controller as Controller;
            var modelState = controller?.ViewData.ModelState;
            if (modelState != null)
            {
                var listError = modelState.ToDictionary(m => m.Key, m => m.Value.Errors
                    .Select(s => s.ErrorMessage)
                    .FirstOrDefault(s => s != null));
                var listErrorJson = await Task.Run(() => JsonConvert.SerializeObject(listError));
                controller.TempData["ModelState"] = listErrorJson;
            }
            await next();
        }
    }
public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute
    {
        public override async Task OnActionExecutionAsync(ActionExecutingContext filterContext, ActionExecutionDelegate next)
        {
            await base.OnActionExecutionAsync(filterContext, next);

            var controller = filterContext.Controller as Controller;
            var tempData = controller?.TempData?.Keys;
            if (controller != null && tempData != null)
            {
                if (tempData.Contains("ModelState"))
                {
                    var modelStateString = controller.TempData["ModelState"].ToString();
                    var listError = await Task.Run(() => 
                        JsonConvert.DeserializeObject<Dictionary<string, string>>(modelStateString));
                    var modelState = new ModelStateDictionary();
                    foreach (var item in listError)
                    {
                        modelState.AddModelError(item.Key, item.Value ?? "");
                    }

                    controller.ViewData.ModelState.Merge(modelState);
                }
            }
            await next();
        }
    }

请告诉我这两个选项哪个更正确?为什么第一个选项不起作用?

【问题讨论】:

  • 在这里使用await next(); 是不正确的,因为它会导致该方法被多次命中。

标签: c# async-await asp.net-core asp.net-core-mvc asp.net-core-1.0


【解决方案1】:

请告诉我这两个选项中哪个更正确。

选项二更正确,因为它确实有效 - 正如您所注意到的。如果您不打算从它继承,他们将使它成为sealed。在这种情况下使用ActionFilterAttribute 似乎是合适的。事实上,quick look @ the repo 表明这实际上是一种非常常见的方法。

为什么第一个选项不起作用?

它没有调用base 调用。关键是您的自定义实现中可能缺少base 调用中的代码。看看here

【讨论】:

    猜你喜欢
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    • 2010-09-21
    • 2016-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多