自定义异步MediaTypeFormatter返回无法访问已关闭的Stream

时间:2016-10-25 14:41:42

标签: c# asynchronous owin mediatypeformatter

我使用自定义MediaTypeFormatter遇到了奇怪的行为,但只有Owin自托管的WebApi。

使用IIS中托管的标准.NET WebApi项目,相同的格式化程序可以正常工作。

逐步执行该程序,Formatter中的CanWriteType被多次调用。异常记录器正在触发,但Cannot access a closed stream.

除外

任何见解都会非常有用!删除此格式化程序的异步性质会使其正常工作,因此很可能是一些奇怪的线程问题。我希望继续使用异步格式化程序,如果可能的话,不要使用BufferedMediaTypeFormatter

堆栈追踪:

at System.IO.__Error.StreamIsClosed()
   at System.IO.MemoryStream.Seek(Int64 offset, SeekOrigin loc)
   at System.Net.Http.HttpContent.<>c__DisplayClass21_0.<LoadIntoBufferAsync>b__0(Task copyTask)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__13.MoveNext()

Startup.cs:

namespace ConsoleApplication1
{

    public class TraceExceptionLogger : ExceptionLogger
    {
        public override void Log(ExceptionLoggerContext context)
        {
           Trace.TraceError(context.ExceptionContext.Exception.ToString());
        }
    }

    public class Startup
    {
        // This code configures Web API. The Startup class is specified as a type
        // parameter in the WebApp.Start method.
        public void Configuration(IAppBuilder appBuilder)
        {
            // Configure Web API for self-host. 
            HttpConfiguration config = new HttpConfiguration();

            // Add in a simple exception tracer so we can see what is causing the 500 Internal Server Error
            config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

            // Add in the custom formatter
            config.Formatters.Add(new JsonFhirAsyncFormatter());

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Formatters.Add(new JsonFhirAsyncFormatter());

            appBuilder.UseWebApi(config);
        }
    }
}

格式化:

namespace ConsoleApplication1
{
    public class JsonFhirAsyncFormatter : MediaTypeFormatter
    {
        public JsonFhirAsyncFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json+fhir"));
        }

        public override bool CanWriteType(Type type)
        {
            return true;
        }

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

        public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
        {
            using (StreamWriter streamwriter = new StreamWriter(writeStream))
            {
                await streamwriter.WriteAsync("{\"test\":\"test\"}");
            }
        }
    }
}

控制器:

namespace ConsoleApplication1.Controllers
{
    [RoutePrefix("test")]
    public class TestController : ApiController
    {
        [HttpGet]
        [Route("")]
        public async Task<string> Test()
        {
            // Eventually this will using an await method
            return "test";
        }
    }
}

的Program.cs:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            WebApp.Start<Startup>("http://localhost:9000");
            Console.ReadLine();
        }
    }
}

1 个答案:

答案 0 :(得分:0)

也许你可以试试这个:

public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        using (StreamWriter streamwriter = new StreamWriter(writeStream))
        {
            return streamwriter.WriteAsync("{\"test\":\"test\"}");
        }
    }

然后,这不会激活异步内容,并允许在需要时发生异步。

但是在我的服务器(sqlonfhir)中,此功能实现为

 public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        StreamWriter writer = new StreamWriter(writeStream);
        JsonWriter jsonwriter = new JsonTextWriter(writer);
        if (type == typeof(OperationOutcome))
        {
            Resource resource = (Resource)value;
            FhirSerializer.SerializeResource(resource, jsonwriter);
        }
        else if (typeof(Resource).IsAssignableFrom(type))
        {
            if (value != null)
            {
                Resource r = value as Resource;
                SummaryType st = SummaryType.False;
                if (r.UserData.ContainsKey("summary-type"))
                    st = (SummaryType)r.UserData["summary-type"];
                FhirSerializer.SerializeResource(r, jsonwriter, st);
            }
        }
        writer.Flush();
        return Task.FromResult<object>(null); // Task.CompletedTask // When we update to .net 4.6;
    }

(是的,我也使用此代码进行OWIN自我测试进行单元测试)