使用 ReadAsAsync 和 WriteToStreamAsync 的 WebApi 格式化程序 - Json 被截断而 xml 工作正常

WebApi Formatter that is using ReadAsAsync and WriteToStreamAsync - Json gets truncated while xml works fine

我正在与 C#WebAPI 2 合作,但出现了奇怪的行为。

我有调用 Controller 的服务,内容有时可以作为 Json 发送,有时可以作为 XML 发送,用于调用的机制如下所示:

using (var client = new HttpClient())
{
    var pricingControllerUrl = CreateEndpoint(apiUrl);
    using (var response = (int)request.Metadata.InputType >= 3 ?  client.PostAsJsonAsync(pricingControllerUrl, request) : client.PostAsXmlWithSerializerAsync(pricingControllerUrl, request))
    {
        if (response.Result.IsSuccessStatusCode)
        {
            var session = response.Result.Content.ReadAsAsync<Session>(new List<MediaTypeFormatter>() { new XmlMediaTypeFormatter { UseXmlSerializer = true }, new JsonMediaTypeFormatter() }).Result;
            return session;
        }
    }
}

public static class HttpExtensions
{
    public static Task<HttpResponseMessage> PostAsXmlWithSerializerAsync<T>(this HttpClient client, string requestUri, T value)
    {
        return client.PostAsync(new Uri(requestUri), value,
                      new XmlMediaTypeFormatter { UseXmlSerializer = true }
                      );
    }
}

在接收端(控制器),

public async Task<IHttpActionResult> PostSession([FromBody] Session session)
    {
    //do the calculations
    return Content(HttpStatusCode.OK, sessionResponse, new ReducedSessionFormatter(), this.Request.Content.Headers.ContentType);
    }

响应必须通过在发送之前删除一些信息来减少,下面的格式化程序用于促进这一点:

public class ReducedSessionFormatter : MediaTypeFormatter
{
    public ReducedSessionFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/json"));
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
    }

    public ReducedSessionFormatter(MediaTypeFormatter formatter) : base(formatter)
    {
    }

    public override bool CanReadType(Type type)
    {
        return false;
    }

    public override bool CanWriteType(Type type)
    {
        return type.IsAssignableFrom(typeof (Session));
    }

    protected XDocument ReduceXml(XDocument doc)
    {

        //removing stuff from xml
        return doc;
    }

    protected JObject ReduceJson(JObject serializedJson)
    {
        //removing stuff from json
        return serializedJson;
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        if (content.Headers.ContentType.MediaType.Contains("xml"))
        {
            var doc = SerializeToXmlDocument(type, value).ToXDocument();
            doc = ReduceXml(doc);

            var settings = new XmlWriterSettings {Encoding = new UTF8Encoding(false)};
            using (XmlWriter w = XmlWriter.Create(writeStream, settings))
            {
                doc.Save(w);
            }
        }
        else
        {
            var json = new JavaScriptSerializer().Serialize(value);
            var serializedJson = (JObject)JsonConvert.DeserializeObject(json);
            var serializedJsonString = ReduceJson(serializedJson).ToString(Newtonsoft.Json.Formatting.None);
            var writer = new StreamWriter(writeStream);
            writer.Write(serializedJsonString);
        }

        var tcs = new TaskCompletionSource<object>();
        tcs.SetResult(null);
        return tcs.Task;
    }

    public XmlDocument SerializeToXmlDocument(Type type, object value)
    {
        var serializer = new XmlSerializer(type);

        XmlDocument xmlDocument = null;

        using (var memoryStream = new MemoryStream())
        {
            serializer.Serialize(memoryStream, value);
            memoryStream.Position = 0;
            using (var xtr = XmlReader.Create(memoryStream, new XmlReaderSettings {IgnoreWhitespace = true}))
            {
                xmlDocument = new XmlDocument();
                xmlDocument.Load(xtr);
            }
        }

        return xmlDocument;
    }
}

public static class XmlExtensions
{
    public static XDocument ToXDocument(this XmlDocument xmlDocument)
    {
        using (var nodeReader = new XmlNodeReader(xmlDocument))
        {
            nodeReader.MoveToContent();
            return XDocument.Load(nodeReader);
        }
    }
}
public static class JsonExtensions
{
    public static bool IsNullOrEmpty(this JToken token)
    {
        return (token == null) ||
               (token.Type == JTokenType.Array && !token.HasValues) ||
               (token.Type == JTokenType.Object && !token.HasValues) ||
               (token.Type == JTokenType.String && token.ToString() == String.Empty) ||
               (token.Type == JTokenType.Null);
    }
}

当在格式化程序中聚合时,xml 和 json 都是有效且正常的,如果数据在不使用格式化程序的情况下发回,一切正常

奇怪的东西: 当我使用格式化程序并发回时 Json 它会被截断,与它的长度无关。即使是非常小的物体(小于 10k 长度)也总是在同一个物体的相同位置被切断,但不同物体的长度不同,并且仅适用于 json 并且适用于 xml...

如果 json 没有像以下那样被字符串化,它也会失败:

            var writer = new StreamWriter(writeStream);
            writer.Write(ReduceJson(serializedJson));

我已经添加了minimal solution to show the issue

这是怎么回事?为什么使用格式化程序会截断 Json 而不是 XML 的响应内容?

好的,我发现问题出在

中任务 .Result; 的使用

var session = response.Result.Content.ReadAsAsync<Session>(new List<MediaTypeFormatter>() { new XmlMediaTypeFormatter { UseXmlSerializer = true }, new JsonMediaTypeFormatter() }).Result;

(以及问题中未列出的其他一些机制)应该 awaited。

.Results 替换为 await 并将链中的所有方法标记为 async - 我开始收到完整的回复。