In a web service, I am getting a "Key Not Found" exception when I try and serialize to a JSON message
I have a web service that uses a call to the System.ServiceModel.Dispatcher.SerializeReply message (specifically the MultiplexingDispatchMessageFormatter implementation). When I make this call, I am getting a "Key not found" exception that I traced to a line in System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter where it is trying to get the "defaultContentType" based on a key.
Unfortunately I'm unable to see what the key is, but the defaultContentType collection does not appear to have any items in it.
What do I need to do to get the SerializeReply working?
The code:
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]
}
This is the line that's blowing up in the System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter (the key is "json", the defaultContentTypes has no e开发者_StackOverflow社区ntries):
outgoingResponse.ContentType = this.defaultContentTypes[key];
And the exception I'm getting:
System.Collections.Generic.KeyNotFoundException occurred
Message=The given key was not present in the dictionary.
Source=mscorlib
StackTrace:
at System.ThrowHelper.ThrowKeyNotFoundException()
InnerException:
I had this problem and figured out how to fix it for my case with good ol' Reflector. The reason was because the ContentType of the OutgoingResponse was empty. Adding the following immediately before you call "SerializeReply" against your jsonResponseDispatchMessageFormatter might do the trick:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
The accepted answer works. However, if you want your endpoint to support XML and JSON, depending on which attribute is configured, below is code that is more robust for either scenario.
/// <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;
}
}
Then, just make sure to call SetContentType()
before you call SerializeReply(...)
. This is mainly applicable if you are calling one of the built-in WCF formatters in a scenario where you're sometimes calling your own formatter, otherwise calling a built-in WCF formatter.
精彩评论