C# 在ASP.NET Web API中更改单个请求的JsonFormatter

C# 在ASP.NET Web API中更改单个请求的JsonFormatter,c#,asp.net,serialization,asp.net-web-api,C#,Asp.net,Serialization,Asp.net Web Api,我有一个定义如下的操作过滤器,在我的Web API项目中全局注册: public class ResultCasingFilter : IActionFilter { private static JsonMediaTypeFormatter _pascalCasingFormatter; private static JsonMediaTypeFormatter _camelCasingFormatter; // Constructor that initialize

我有一个定义如下的操作过滤器,在我的Web API项目中全局注册:

public class ResultCasingFilter : IActionFilter
{
    private static JsonMediaTypeFormatter _pascalCasingFormatter;
    private static JsonMediaTypeFormatter _camelCasingFormatter;

    // Constructor that initializes formatters left out for brevity

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        actionContext.RequestContext.Configuration.Formatters.Clear();
        actionContext.RequestContext.Configuration.Formatters.Add(
            ResponseShouldBePascalCased(actionContext)
            ? _pascalCasingFormatter
            : _camelCasingFormatter);
        return continuation();
    }

    private static bool ResponseShouldBePascalCased(HttpActionContext actionContext)
    {
        return actionContext.ActionDescriptor
            .GetCustomAttributes<PascalCasedResultAttribute>().Any();
    }

    public bool AllowMultiple { get { return false; } }
}
public类ResultCasingFilter:IActionFilter
{
私有静态JsonMediaTypeFormatter\u pascalCasingFormatter;
私有静态JsonMediaTypeFormatter\u camelCasingFormatter;
//用于初始化为简洁起见遗漏的格式化程序的构造函数
公共任务ExecuteActionFilterAsync(HttpActionContext actionContext、CancellationToken CancellationToken、Func continuation)
{
actionContext.RequestContext.Configuration.Formatters.Clear();
actionContext.RequestContext.Configuration.Formatters.Add(
ResponseShouldBePascalCased(actionContext)
?帕斯卡格式化程序
:_camelCasingFormatter);
返回continuation();
}
私有静态bool响应shouldbepascalcased(HttpActionContext-actionContext)
{
返回actionContext.ActionDescriptor
.GetCustomAttributes().Any();
}
public bool AllowMultiple{get{return false;}
}
这很有效,但我似乎在请求之间受到干扰;如果我一次向操作方法发出一个请求,其中一个具有
PascalCasedResultAttribute
,而另一个没有,那么一切都会按预期进行-但是如果我发出两个非常接近的请求,那么两个请求有时会以相同的大小写结束

我将此行为解释为对
actionContext.RequestContext.Configuration.Formatters
的更改确实会更改整个应用程序的配置,而不仅仅是当前请求,有时请求会重叠。基本上,我的解决方案基于以下事件序列:

  • 请求1选择其序列化程序
  • 使用最后选择的序列化程序序列化请求1
  • 请求2选择其序列化程序
  • 使用最后选择的序列化程序序列化请求2
  • 请注意,如果第二步和第三步更改了顺序,则会更改行为。我想要的是

  • 请求1选择其序列化程序
  • 使用序列化程序1序列化请求1
  • 请求2选择其序列化程序
  • 使用序列化程序2序列化请求2
  • 其中,我(或框架)可以在不改变行为的情况下切换2和3的顺序


    如何才能最好地实现这一点?

    问题是您正在更改一个全局变量,这显然与排序不一致

    您可以在操作中手动序列化,并根据需要返回字符串


    然后,您可以提供一个标志来选择在请求中使用哪个序列化程序,也可以使用cookies来记住客户端的选择。

    我最后做了以下操作:

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        // Let the action method execute, resulting in a serialized response
        var responseMessage = await continuation();
    
        if (responseMessage.Content is ObjectContent)
        {
            // Get the message content in its unserialized form, and choose formatter
            var content = responseMessage.Content as ObjectContent;
            var formatter = ResponseShouldBePascalCased(actionContext)
                            ? _pascalCasingFormatter
                            : _camelCasingFormatter;
    
            // Re-serialize content, with the correctly chosen formatter
            responseMessage.Content = new ObjectContent(content.ObjectType, content.Value, 
                                                        formatter);
        }
        // Return the (possibly) re-serialized message
        return responseMessage;
    }
    
    public Task ExecuteActionFilterAsync(HttpActionContext actionContext、CancellationToken CancellationToken、Func continuation)
    {
    //让action方法执行,从而产生序列化响应
    var responseMessage=等待继续();
    if(responseMessage.Content是ObjectContent)
    {
    //以未序列化的形式获取消息内容,然后选择formatter
    var content=responseMessage.content作为ObjectContent;
    var formatter=ResponseShouldBePascalCased(actionContext)
    ?帕斯卡格式化程序
    :_camelCasingFormatter;
    //使用正确选择的格式化程序重新序列化内容
    responseMessage.Content=新对象内容(Content.ObjectType、Content.Value、,
    格式化程序);
    }
    //返回(可能)重新序列化的消息
    返回响应消息;
    }
    
    在找到这个解决方案之前,我要跳过的主要障碍是认识到我可以
    等待continuation()
    让action方法执行,然后处理响应


    这种方法还有一个缺点,即如果客户机请求XML,它仍然会得到JSON,因为我手动选择序列化程序,而不查看
    Accepts
    头。在我的用例中,我完全同意这一点,因为我们只使用JSON,但如果这对您来说很重要,那么您需要更复杂的东西来代替我选择格式化程序的三元语句。(如果有一种简单的方法只对序列化为给定格式的内容执行此操作,我很乐意了解!)

    问题是,由于这确实是一个交叉问题,我不想要求对操作方法进行任何更改,只需添加属性即可。有没有一种方法可以在
    IActionFilter
    中手动序列化,而不是在动作本身中?实际上,我认为在过滤器中有一种很好的方法<代码>var响应=等待继续()应该在action方法返回时给我响应消息。但是,此时正文已经序列化。。。