2016-10-25 4 views
1

カスタムMediaTypeFormatterでは奇妙な動作が発生していますが、Owin Self-hosted WebApiでのみ発生します。カスタム非同期MediaTypeFormatterが返され、閉じられたストリームにアクセスできない

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

あなたが達成するために何をしようとしているとして実装されている

...あなたのコードに基づいて、あなたは車輪を再発明しようとしているようです...あなたは、OWINの自己ホストWeb APIを使って、それを試してみてください。 – Hackerman

+0

私はOWINを使ってWeb apiをホストしています。だからこそ、私は「Owin Self-hosted WebApiだけで」と言ったのです。これはOWINを使用しています。この質問は、OwinスタートアップクラスであるStartup.csクラスのカスタムMediaTypeFormatterでのスレッド問題に関するものです。私は具体的にOWINの指示に従ってhttps://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-apiにアクセスし、サンプルプロジェクトを作成しました。問題が何であるかを示します。 –

+0

ええ、私の悪い....私はあなたの質問の真ん中まで読んだだけです...確かに、その記事は2013年からの日付です、そして時代遅れのコードかもしれません...私はこの1つを試して、魅力のように動作します..コードはgithubでも利用できます:http://codeopinion.com/self-host-asp-net-web-api/ – Hackerman

答えて

1

たぶん、あなたはこの試みることができる:

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セルフ・テストを使用して、私のユニットテストを行う)

+0

それはそれを行いました。 writer.Flush();のように見えます。 return Task.FromResult (null); は魔法でした! ここに他のFHIRユーザーを見てうれしい!ありがとうブライアン –

関連する問題