在web服务中,我得到“未找到密钥”.当我尝试序列化到JSON消息时,会出现异常

本文关键字:JSON 序列化 消息 异常 未找到密钥 服务 web 密钥 | 更新日期: 2023-09-27 18:07:50

我有一个web服务,它使用对System.ServiceModel.Dispatcher.SerializeReply消息的调用(特别是MultiplexingDispatchMessageFormatter实现)。当我进行此调用时,我得到一个"未找到键"异常,我跟踪到System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter中的一行,它试图基于键获取"defaultContentType"。

不幸的是,我无法看到关键是什么,但defaultContentType集合似乎没有任何项目在它。

我需要做什么才能让SerializeReply工作?

代码:

public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
    System.Web.HttpRequest requestMessage = System.Web.HttpContext.Current.Request;
    string format = requestMessage.QueryString["format"];
    if (!string.IsNullOrEmpty(format) && string.Compare(format, "json", true) == 0)
    {
    return jsonResponseDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
    }
    //[UNRELATED CODE]
}

这是在System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter中出现的行(关键是"json", defaultContentTypes没有条目):

outgoingResponse.ContentType = this.defaultContentTypes[key];

我得到的例外是:

System.Collections.Generic.KeyNotFoundException occurred
Message=The given key was not present in the dictionary.
Source=mscorlib
StackTrace:
   at System.ThrowHelper.ThrowKeyNotFoundException()
InnerException: 

在web服务中,我得到“未找到密钥”.当我尝试序列化到JSON消息时,会出现异常

我有这个问题,并找出了如何解决它为我的情况与良好的ol' Reflector。原因是OutgoingResponse的ContentType为空。在对jsonResponseDispatchMessageFormatter调用"SerializeReply"之前立即添加以下代码可能会奏效:

WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";

接受的答案有效。但是,如果您希望端点支持XML和JSON(取决于配置的属性),则下面的代码对这两种场景都更健壮。

    /// <summary>
    /// Try to set the content type as close to the user's preference as we can get.
    /// </summary>
    private void SetContentType()
    {
        if (WebOperationContext.Current == null)
        {
            throw new InvalidOperationException("There is no WebOperationContext. This class expects to operate within a WCF context.");
        }
        WebMessageFormat contentType = WebMessageFormat.Json;
        //first let's grab the default, if possible
        if (_serviceEndpoint.EndpointBehaviors.Contains(typeof (WebHttpBehavior)))
        {
            var behavior = (WebHttpBehavior)_serviceEndpoint.EndpointBehaviors[typeof (WebHttpBehavior)];
            contentType = behavior.DefaultOutgoingResponseFormat;
        }
        else if (_serviceEndpoint.EndpointBehaviors.Contains(typeof(WebHttpByteResponseBehavior)))
        {
            var behavior = (WebHttpByteResponseBehavior)_serviceEndpoint.EndpointBehaviors[typeof(WebHttpByteResponseBehavior)];
            contentType = behavior.DefaultOutgoingResponseFormat;
        }
        //then let's see if an explicit override is available
        if (_operationDescription.OperationBehaviors.Contains(typeof (WebInvokeAttribute)))
        {
            var behavior = (WebInvokeAttribute)_operationDescription.OperationBehaviors[typeof(WebInvokeAttribute)];
            if (behavior.IsResponseFormatSetExplicitly)
            {
                contentType = behavior.ResponseFormat;
            }
        }
        else if (_operationDescription.OperationBehaviors.Contains(typeof(WebGetAttribute)))
        {
            var behavior = (WebGetAttribute)_operationDescription.OperationBehaviors[typeof(WebGetAttribute)];
            if (behavior.IsResponseFormatSetExplicitly)
            {
                contentType = behavior.ResponseFormat;
            }
        }
        //finally set the content type based on whatever we found
        WebOperationContext.Current.OutgoingResponse.ContentType = MapToStringContentType(contentType);
    }
    /// <summary>
    /// Maps from a WebMessageFormat to a valid Content-Type header value.
    /// </summary>
    /// <param name="contentType"></param>
    /// <returns></returns>
    private string MapToStringContentType(WebMessageFormat contentType)
    {
        switch (contentType)
        {
            case WebMessageFormat.Xml:
                return TEXT_XML;
                break;
            case WebMessageFormat.Json:
                return APPLICATION_JSON;
                break;
            default:
                return APPLICATION_JSON;
                break;
        }
    }

那么,请确保在呼叫SerializeReply(...)之前先呼叫SetContentType()。这主要适用于在调用内置WCF格式化程序的情况下,有时调用自己的格式化程序,否则调用内置WCF格式化程序。