WCF / SVC Returns a HTML error page instead of JSON when StreamReader used

356 Views Asked by At

it would be great if someone could finally put me out of my misery with this, it has been driving me nuts!

I'm currently writing a simple WCF / SVC REST service and when an exception is thrown, I would like to return meaningful JSON with a HTTP status code.

This all seems to work fine.

However, when I try to read form data (x-www-form-urlencoded) from a POST request, the response is a generic HTML page and not JSON. This only occurs when I read the stream using StreamReader.

Service1.svc.cs

public class Service1 : IService1
{
public Customer DoWork2(Stream stream)
        {
            // ************************************************************
            // If this block of code is commented out, the response is JSON.
            using (var sr = new StreamReader(stream)) // <-- Using stream reader is what causes it to return HTML error page!
            {
                var body = sr.ReadToEnd();
                var nvc = HttpUtility.ParseQueryString(body);
            }
            // ************************************************************

            var errorData = new ErrorData("example error", "stuff");
            throw new WebFaultException<ErrorData>(errorData, HttpStatusCode.BadRequest);
        }

}

iService.cs

[WebInvoke(Method = "POST", UriTemplate = "/DoWork2", ResponseFormat = WebMessageFormat.Json)]
        Service1.Customer DoWork2(Stream stream);

web.config

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  https://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.6" />
    <httpRuntime targetFramework="4.6" />
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
  <system.serviceModel>
    <services>
      <service name="RestfulServiceExample.Service1" behaviorConfiguration="ServiceBehaviour">
        <endpoint address="" contract="RestfulServiceExample.IService1" behaviorConfiguration="webHttp" binding="webHttpBinding" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviour">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>

        <!--<behavior>
          --><!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --><!--
          <serviceMetadata httpGetEnabled="true"/>
          --><!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information --><!--
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>-->


      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="webHttp">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <!--<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      multipleSiteBindingsEnabled="true" />-->
  </system.serviceModel>
  <!--<system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>-->
</configuration>

Does anyone know why this is occurring and how I might be able to solve this please?

Thanks!

1

There are 1 best solutions below

0
Dom On

After tearing my hair out, I have got to the bottom of the problem after getting a few hints here and there from other pages.

One of them being this post here... Wrong WebFaultException when using a Stream and closing the stream

This happens because the StreamReader takes over 'ownership' of the stream. In other words, it makes itself responsible for closing the source stream. As soon as your program calls Dispose or Close (leaving the using statement scope in your case) then it will dispose the source stream as well. Calling sr.Dispose() in your case. So the file stream is dead after.

To get round this problem, I added another stream variable and copied the original stream to this stream. i.e.

var isolatedStream = new MemoryStream();
stream.CopyTo(isolatedStream);

So the method now looks like this...

public Customer DoWork2(Stream stream)
        {
            var isolatedStream = new MemoryStream();
            stream.CopyTo(isolatedStream);

            using (var sr = new StreamReader(isolatedStream)) // <-- Using stream reader is what causes it to return HTML error page!
            {
                var body = sr.ReadToEnd();
                var nvc = HttpUtility.ParseQueryString(body);
            }
            // ************************************************************

            var errorData = new ErrorData("example error", "stuff");
            throw new WebFaultException<ErrorData>(errorData, HttpStatusCode.BadRequest);
        }

The response is now returning a JSON error response as originally expected.