是否可以阻止某个操作成为ContentResult
本文关键字:操作 ContentResult 是否 | 更新日期: 2023-09-27 17:53:23
我正在尝试编写一个过滤器,该过滤器包装数据以遵循JSON API规范,到目前为止,我已经让它处理了所有直接返回ActionResult的情况,例如ComplexTypeJSON
。我正在尝试让它在ComplexType
这样的情况下工作,在这种情况下我不必经常运行Json
函数。
[JSONAPIFilter]
public IEnumerable<string> ComplexType()
{
return new List<string>() { "hello", "world" };
}
[JSONAPIFilter]
public JsonResult ComplexTypeJSON()
{
return Json(new List<string>() { "hello", "world" });
}
然而,当我导航到ComplexType
时,当public override void OnActionExecuted(ActionExecutedContext filterContext)
运行时,filterContext.Result
是一个ContentResult,它只是一个字符串,其中filterContext.Result.Content
只是:
"System.Collections.Generic.List`1[System.String]"
有没有办法让ComplexType
变成JsonResult
而不是ContentResult
?
对于上下文,以下是确切的文件:
TestController.cs
namespace MyProject.Controllers
{
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using MyProject.Filters;
public class TestController : Controller
{
[JSONAPIFilter]
public IEnumerable<string> ComplexType()
{
return new List<string>() { "hello", "world" };
}
[JSONAPIFilter]
public JsonResult ComplexTypeJSON()
{
return Json(new List<string>() { "hello", "world" });
}
// GET: Test
[JSONAPIFilter]
public ActionResult Index()
{
return Json(new { foo = "bar", bizz = "buzz" });
}
[JSONAPIFilter]
public string SimpleType()
{
return "foo";
}
[JSONAPIFilter]
public ActionResult Throw()
{
throw new InvalidOperationException("Some issue");
}
}
}
JSONApiFilter.cs
namespace MyProject.Filters
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MyProject.Exceptions;
using MyProject.Models.JSONAPI;
public class JSONAPIFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
private static readonly ISet<Type> IgnoredTypes = new HashSet<Type>()
{
typeof(FileResult),
typeof(JavaScriptResult),
typeof(HttpStatusCodeResult),
typeof(EmptyResult),
typeof(RedirectResult),
typeof(ViewResultBase),
typeof(RedirectToRouteResult)
};
private static readonly Type JsonErrorType = typeof(ErrorModel);
private static readonly Type JsonModelType = typeof(ResultModel);
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (IgnoredTypes.Any(x => x.IsInstanceOfType(filterContext.Result)))
{
base.OnActionExecuted(filterContext);
return;
}
var resultModel = ComposeResultModel(filterContext.Result);
var newJsonResult = new JsonResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = resultModel
};
filterContext.Result = newJsonResult;
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
if (modelState == null || modelState.IsValid)
{
base.OnActionExecuting(filterContext);
}
else
{
throw new ModelStateException("Errors in ModelState");
}
}
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception == null) return;
// Todo: if modelstate error, do not provide that message
// set status code to 404
var errors = new List<string>();
if (!(filterContext.Exception is ModelStateException))
{
errors.Add(filterContext.Exception.Message);
}
var modelState = filterContext.Controller.ViewData.ModelState;
var modelStateErrors = modelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToList();
if (modelStateErrors.Any()) errors.AddRange(modelStateErrors);
var errorCode = (int)System.Net.HttpStatusCode.InternalServerError;
var errorModel = new ErrorModel()
{
status = errorCode.ToString(),
detail = filterContext.Exception.StackTrace,
errors = errors,
id = Guid.NewGuid(),
title = filterContext.Exception.GetType().ToString()
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = errorCode;
var newResult = new JsonResult() { Data = errorModel, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
filterContext.Result = newResult;
}
private ResultModel ComposeResultModel(ActionResult actionResult)
{
var newModelData = new ResultModel() { };
var asContentResult = actionResult as ContentResult;
if (asContentResult != null)
{
newModelData.data = asContentResult.Content;
return newModelData;
}
var asJsonResult = actionResult as JsonResult;
if (asJsonResult == null) return newModelData;
var dataType = asJsonResult.Data.GetType();
if (dataType != JsonModelType)
{
newModelData.data = asJsonResult.Data;
}
else
{
newModelData = asJsonResult.Data as ResultModel;
}
return newModelData;
}
}
}
有两个选项:
1.使用ApiController而不是控制器
apicontroller将返回json结果,默认的序列化程序是Newtonsoft.json(此处(,因此您可以如下使用:
//the response type
public class SimpleRes
{
[JsonProperty(PropertyName = "result")]
public string Result;
}
//the controller
public class TestController : ApiController
{
[HttpGet]
[HttpPost]
[JSONAPIFilter]
public SimpleRes TestAction()
{
return new SimpleRes(){Result = "hello world!"};
}
}
2.如果您坚持使用控制器,请使用您自己的ActionResult包装您的响应:
//json container
public class AjaxMessageContainer<T>
{
[JsonProperty(PropertyName = "result")]
public T Result { set; get; }
}
//your own actionresult
public class AjaxResult<T> : ActionResult
{
private readonly T _result;
public AjaxResult(T result)
{
_result = result;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = "application/json";
var result = JsonConvert.SerializeObject(new AjaxMessageContainer<T>
{
Result = _result,
});
var bytes =
new UTF8Encoding().GetBytes(result);
context.HttpContext.Response.OutputStream.Write(bytes, 0, bytes.Length);
}
}
//your controller
[JSONAPIFilter]
public AjaxResult<List<String>> TestSimple()
{
return AjaxResult<List<String>>(new List<string>() { "hello", "world" });
}
如果你想从过滤器中获得日志或其他内容的响应字符串:
var result = filterContext.Response.Content.ReadAsStringAsync();
我想这就是您想要的:
public class JSONAPIFilterAttribute : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
context.Result = new JsonResult
{
Data = ((ViewResult)context.Result).ViewData.Model
};
}
}
来自@resteronacid:在actionfilter 中返回jsonresult
我刚刚遇到了同样的问题,发现了一种稍微不同的方法。基本的想法来自NOtherDev。我将介绍一款IActionInvoker
。
public class ControllerActionInvokerWithDefaultJsonResult : ControllerActionInvoker
{
public const string JsonContentType = "application/json";
protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
{
if (controllerContext.HttpContext.Request.Path.StartsWith("/api/"))
{
return (actionReturnValue as ActionResult)
?? new JsonResult
{
Data = actionReturnValue,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
return base.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue);
}
}
在这种情况下,每个以"/api/"开头的请求都会将结果转换为json,但前提是actionReturnValue
不是从ActionResult
继承的类型。
IActionInvoker
由DependencyResolver
解析,因此您需要在您最喜欢的ioc容器中定义注册,该容器已设置为DependencyResolver
。
myFavoriteContainer.Register<IActionInvoker, ControllerActionInvokerWithDefaultJsonResult>(Lifestyle.Transient);
对于JsonResult
,您可以使用内置或此。
在使用异步操作方法的情况下,您应该从AsyncControllerActionInvoker
而不是ControllerActionInvoker
继承,我认为您还需要为IAsyncActionInvoker
添加另一个注册。我不确定调用程序本身异步部分的变化。