我使用自定义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();
}
}
}
答案 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自我测试进行单元测试)