如何将转换为Message
之前将传入的JSON修改为WCF REST服务?
例如,如果我提交以下内容:
{
"Name": "Joe Bloggs",
"Age": 30
}
我希望剥离所有空格,以便将数据包主体转换为:
{"Name":"Joe Bloggs","Age":30}
我正在尝试解决a problem in System.Runtime.Serialization.Json.XmlJsonReader
我发现如果数据包中有任何空格,JSON没有正确转换为XML的地方。由于我不能保证我的所有客户端都会发送无空格的JSON,我希望某种预处理器能够在传递给XmlJsonReader
之前从JSON中删除空格。
我已尝试使用IDispatchMessageInspector
方法实现自定义AfterReceiveRequest
。但这已经太晚了,因为JSON已经转换为包含不正确XML的Message
。我需要在此阶段之前修改JSON,但是我找不到任何可追溯性的点。
答案 0 :(得分:1)
如果要在解码之前处理消息,则需要一个自定义消息编码器(这是在线路中的字节和消息对象之间进行转换的组件)。您可以在http://blogs.msdn.com/b/carlosfigueira/archive/2011/11/09/wcf-extensibility-message-encoders.aspx找到有关自定义编码器的更多信息。
下面的自定义编码器会从JSON文档中删除空白区域。由JsonReaderWriterFactory.CreateJsonWriter
创建的默认编写器不会进行任何漂亮的打印,因此这基本上就是您所需要的。
public class StackOverflow_8670954
{
[DataContract(Name = "Person", Namespace = "MyNamespace")]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
public override string ToString()
{
return string.Format("Person[Name={0},Age={1}]", Name, Age);
}
}
[DataContract(Name = "Employee", Namespace = "MyNamespace")]
public class Employee : Person
{
[DataMember]
public int EmployeeId { get; set; }
public override string ToString()
{
return string.Format("Employee[Id={0},Name={1}]", EmployeeId, Name);
}
}
[ServiceContract]
public interface ITest
{
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[ServiceKnownType(typeof(Employee))]
string PrintPerson(Person person);
}
public class Service : ITest
{
public string PrintPerson(Person person)
{
return person.ToString();
}
}
static Binding GetBinding()
{
var result = new CustomBinding(new WebHttpBinding());
for (int i = 0; i < result.Elements.Count; i++)
{
MessageEncodingBindingElement mebe = result.Elements[i] as MessageEncodingBindingElement;
if (mebe != null)
{
result.Elements[i] = new MyEncodingBindingElement(mebe);
break;
}
}
return result;
}
class MyEncodingBindingElement : MessageEncodingBindingElement
{
MessageEncodingBindingElement inner;
public MyEncodingBindingElement(MessageEncodingBindingElement inner)
{
this.inner = inner;
}
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new MyEncoderFactory(this.inner.CreateMessageEncoderFactory());
}
public override MessageVersion MessageVersion
{
get { return this.inner.MessageVersion; }
set { this.inner.MessageVersion = value; }
}
public override BindingElement Clone()
{
return new MyEncodingBindingElement(this.inner);
}
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
{
return context.CanBuildInnerChannelListener<TChannel>();
}
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
context.BindingParameters.Add(this);
return context.BuildInnerChannelListener<TChannel>();
}
class MyEncoderFactory : MessageEncoderFactory
{
private MessageEncoderFactory inner;
public MyEncoderFactory(MessageEncoderFactory inner)
{
this.inner = inner;
}
public override MessageEncoder Encoder
{
get { return new MyEncoder(this.inner.Encoder); }
}
public override MessageVersion MessageVersion
{
get { return this.inner.MessageVersion; }
}
}
class MyEncoder : MessageEncoder
{
private MessageEncoder inner;
public MyEncoder(MessageEncoder inner)
{
this.inner = inner;
}
public override string ContentType
{
get { throw new NotImplementedException(); }
}
public override string MediaType
{
get { throw new NotImplementedException(); }
}
public override MessageVersion MessageVersion
{
get { throw new NotImplementedException(); }
}
public override bool IsContentTypeSupported(string contentType)
{
return this.inner.IsContentTypeSupported(contentType);
}
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
if (IsJsonContentType(contentType))
{
// the encoder can also support other types of content (raw, xml), so we don't want to deal with those
MemoryStream writeStream = new MemoryStream();
XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(writeStream, Encoding.UTF8, false);
XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(buffer.Array, buffer.Offset, buffer.Count, XmlDictionaryReaderQuotas.Max);
jsonWriter.WriteNode(jsonReader, true);
jsonWriter.Flush();
byte[] newBuffer = bufferManager.TakeBuffer(buffer.Offset + (int)writeStream.Position);
Array.Copy(writeStream.GetBuffer(), 0, newBuffer, buffer.Offset, (int)writeStream.Position);
bufferManager.ReturnBuffer(buffer.Array);
buffer = new ArraySegment<byte>(newBuffer, buffer.Offset, (int)writeStream.Position);
writeStream.Dispose();
jsonReader.Close();
jsonWriter.Close();
}
return this.inner.ReadMessage(buffer, bufferManager, contentType);
}
static bool IsJsonContentType(string contentType)
{
return contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase) ||
contentType.StartsWith("text/json", StringComparison.OrdinalIgnoreCase);
}
public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
{
throw new NotSupportedException("Streamed transfer not supported");
}
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
return this.inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset);
}
public override void WriteMessage(Message message, Stream stream)
{
throw new NotSupportedException("Streamed transfer not supported");
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), GetBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
string[] inputs = new string[]
{
"{\"__type\":\"Employee:MyNamespace\",\"Age\":33,\"Name\":\"John\",\"EmployeeId\":123}",
"{ \"__type\":\"Employee:MyNamespace\",\"Age\":33,\"Name\":\"John\",\"EmployeeId\":123}",
};
foreach (string input in inputs)
{
WebClient c = new WebClient();
c.Headers[HttpRequestHeader.ContentType] = "application/json";
Console.WriteLine(c.UploadString(baseAddress + "/PrintPerson", input));
}
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}