ASP.NET Web API 2 cannot return stream when NET St

2019-04-15 09:56发布

This is one of the weird issue I have seen and here are the steps to reproduce

1> Create new Web API project in VS 2017 with Target Framework 4.6.2

Create new ASP.NET Web Application -> Select Empty -> Check Web API -> Click OK  

2> Add Controller that returns a Stream

    public class ReportController : ApiController
    {
        [HttpGet]
        public HttpResponseMessage Download()
        {
            var filePath = "C:\\Report.xlsx";
            var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            var result = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StreamContent(fs)
            };
            result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            return result;
        }
    }

3> Run the application and invoke the api in IE 11 (use correct port #)
http://localhost:40005/api/report/download

The browser should download a EXCEL file and you should be able to open EXCEL file.

5> Stop Visual Studio

5> In the same solution create a new project with target framework .NET Standard 1.6

6> In the Web API Project, add project reference to newly created NET Standard library project

7> Run the application and invoke api in IE 11
http://localhost:40005/api/report/download

Notice that browser will get json back instead of excel file.

{"Version":{"_Major":1,"_Minor":1,"_Build":-1,"_Revision":-1},"Content":{"Headers":[{"Key":"Content-Type","Value":["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]}]},"StatusCode":200,"ReasonPhrase":"OK","Headers":[],"RequestMessage":null,"IsSuccessStatusCode":true}

If you try with google chrome then instead of json I get exception

Type 'System.Net.Http.StreamContent' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.

I don't know why just by referencing .NET Standard 1.6 project changes the behavior. If I remove the reference to .NET Standard 1.6 project then everything works fine.

Is this a bug .NET?

What is the solution? Because i wanted to create a library that can be shared between classic .NET 4.6.2 and .NET Core projects. and i thought .NET Standard 1.6 is way to go

Update1

When i tried @Nkosi suggestion i get the exception below

HTTP/1.1 500 Internal Server Error Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-SourceFiles: =?UTF-8?B?QzpcTXlQcm9qZWN0c1xXZWJBcHBsaWNhdGlvbjFcV2ViQXBwbGljYXRpb24xXGFwaVxyZXBvcnRcZG93bmxvYWQ=?= X-Powered-By: ASP.NET Date: Sat, 10 Feb 2018 02:02:47 GMT Content-Length: 1937

{"Message":"An error has occurred.","ExceptionMessage":"Method not found: 'System.Web.Http.Results.ResponseMessageResult System.Web.Http.ApiController.ResponseMessage(System.Net.Http.HttpResponseMessage)'.","ExceptionType":"System.MissingMethodException","StackTrace":" at WebApplication1.Controllers.ReportController.Download()\r\n at lambda_method(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"}

Update 2
I lowered down NET Standard version to 1.4 and it worked. However then I changed Web Api project's package management format from package.config to PackageReference and restored all the packages. ( Why? missing dependencies issue) and again API started returning JSON

1条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-04-15 10:44

If you take a look at the .NET Standard compatibility chart, it specifically states:

  • .NET Framework (with .NET Core 1.x SDK)
  • .NET Framework (with .NET Core 2.0 SDK) - I have also read somewhere this also requires VS 2017 to work

These are dependencies that must be present on the development system that consumes the .NET Standard libraries in order for them to be compatible with .NET Framework.

Do note that .NET Framework 4.7.1 patches this behavior so it supports .NET Standard natively, without dependencies (at least it will when this bug is patched).

查看更多
登录 后发表回答