2016-10-25 2 views
1

Je rencontre un comportement étrange avec un MediaTypeFormatter personnalisé, mais uniquement avec un WebApi auto-hébergé par Owin.Custom async MediaTypeFormatter return Impossible d'accéder à un flux fermé

Avec un projet .NET WebApi standard hébergé dans IIS, le même formateur fonctionne correctement.

En passant par le programme, CanWriteType dans le Formatter est appelé plusieurs fois. L'enregistreur d'exceptions est en cours de déclenchement avec une exception de Cannot access a closed stream.

Toute idée serait très utile! En supprimant la nature asynchrone de ce formateur, il fonctionne correctement, c'est donc probablement un problème de threading étrange. Je voudrais rester avec un formateur asynchrone et ne pas utiliser le BufferedMediaTypeFormatter si possible.

Stack Trace:

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); 
     } 
    } 
} 

Formatter:

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\"}"); 
      } 
     } 
    } 
} 

Controller:

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(); 
     } 
    } 
} 
+0

Qu'est-ce que vous essayez d'accomplir ... en fonction de votre code, il semble que vous essayez de réinventer la roue ... vous pouvez accomplir quelque chose comme ça en utilisant l'API web hébergée par OWIN, faites un essai – Hackerman

+0

J'utilise OWIN pour héberger l'API web. C'est pourquoi j'ai dit "mais seulement avec un WebApi auto-hébergé Owin." Ceci utilise OWIN. La question est liée à un problème de threading avec le Custom MediaTypeFormatter dans la classe Startup.cs qui est la classe de démarrage Owin. J'ai spécifiquement suivi les instructions OWIN ici https://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api pour créer un exemple de projet à montrez quel est le problème. –

+0

Mmm, mon mauvais .... Je viens de lire jusqu'au milieu de votre question .... ok, cet article date de 2013, et c'est peut-être un code obsolète ... J'ai essayé celui-ci et fonctionne comme un charme .. aussi le code est disponible sur github: http://codeopinion.com/self-host-asp-net-web-api/ – Hackerman

Répondre

1

Peut-être que vous pourriez essayer ceci:

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\"}"); 
     } 
    } 

Il est ensuite ne pas activer l'étoffe Async, et permet au Async de se produire en cas de besoin.

Cependant, dans mon serveur (sqlonfhir) cette fonction est implémentée comme

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; 
    } 

(Et oui, je fais mes tests unitaires en utilisant des tests d'auto OWIN avec ce code)

+0

Cela l'a fait. Ressembles à writer.Flush(); return Task.FromResult (null); était la magie! Content de voir d'autres utilisateurs de FHIR ici! Merci Brian –