How to get service to return a byte[]

Hi there,

I am trying to have a service return a byte[]. However, I can see that the service GET call finishes, but the request never ends (both in Postman and in the browser). Am I setting this up correctly?

public object Get(GetReport request)
{
    string mimeType = "application/pdf";
    var bytes = _service.GetReport(request.Id);
    return new HttpResult(bytes) { ContentType = mimeType };
}

Thanks,
Leeny

You can just return it https://docs.servicestack.net/service-return-types

I’m assuming you have a different error like your GetReport() implementation hanging or not completing.

Thanks for the link.
I have now tried a simple one like this and it’s the same - Completes the return statement and the request just never seems to end. I do not have any code that is meant to run Post-Requests that I am aware of.

Any suggestions would be appreciated.

		[AddHeader(ContentType = "text/plain")]
		public string Get(Hello request)
		{
			return $"<h1>Hello!</h1>";
		}

Have a look at the raw HTTP Response Headers.

Thanks Demis. Can’t see any response headers as the request just stays as pending until I cancel it in Postman and the browser. Sometimes, in the browser, I get a 404 - Resource not found but this is after it hits and completes the return statement in the Get code.
I will keep digging but if you see something obvious, please let me know.

Thanks.

There’s nothing wrong with the code. See if you can repro the behavior with a new stand-alone project.

1 Like

Hi Demis,

I have simulated the issue in a bare-minimum Visual Studio 2017 project. What would be the best way to send that across?

Thanks,
Leeny

Add it to a GitHub repo and post the link here

https://github.com/LSamuel/ReportSampleAPI/tree/master/ReportSampleAPI

Appreciate it.

It’s because you’ve registered a conflicting HttpHandlerFactory. Please see the docs on
Run side-by-side with another Framework with how to properly configure ServiceStack when hosted on a custom path e.g. it will work if you add the documented Web.config configuration:

<location path="api">
    <system.web>
      <httpHandlers>
        <add path="*" type="ServiceStack.HttpHandlerFactory, ServiceStack" verb="*"/>
      </httpHandlers>
    </system.web>

    <!-- Required for IIS 7.0 -->
    <system.webServer>
      <modules runAllManagedModulesForAllRequests="true"/>
      <validation validateIntegratedModeConfiguration="false" />
      <handlers>
        <add path="*" name="ServiceStack.Factory" 
             type="ServiceStack.HttpHandlerFactory, ServiceStack" 
             verb="*" preCondition="integratedMode" 
             resourceType="Unspecified" allowPathInfo="true" />
      </handlers>
    </system.webServer>
  </location>

and comment out your existing registration:

<!-- <handlers> -->
<!-- 	<add path="*" name="ServiceStack.Factory" type="Eros.Saguna.Common.WebAPI.Security.SessionHttpHandlerFactory, ReportSampleAPI, Version=1.0.0.0, Culture=neutral" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" /> -->
<!-- </handlers> -->

You’ll then be able to call your services from /api, e.g:

/api/hello/{Guid}

ok I can see what you’re trying to do, note the decorated HttpHandlerFactory needs to be async, e.g:

// This class is required to allow Http Sessions to be available to Service Stack
public class SessionHttpHandlerFactory : IHttpHandlerFactory
{
	private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();
	public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
	{
		var handler = Factory.GetHandler(context, requestType, url, pathTranslated);
		return handler == null ? null : new SessionHandlerDecorator((IHttpAsyncHandler)handler);
	}
	public void ReleaseHandler(IHttpHandler handler) => Factory.ReleaseHandler(handler);
}

public class SessionHandlerDecorator : IHttpAsyncHandler, IRequiresSessionState
{
	private IHttpAsyncHandler Handler { get; set; }
	internal SessionHandlerDecorator(IHttpAsyncHandler handler) => Handler = handler;
	public bool IsReusable => Handler.IsReusable;
	public void ProcessRequest(HttpContext context) => Handler.ProcessRequest(context);
	public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) => 
		Handler.BeginProcessRequest(context, cb, extraData);
	public void EndProcessRequest(IAsyncResult result) => Handler.EndProcessRequest(result);
}

Thanks Demis. You are a super star! That worked perfectly.

1 Like

Hi Demis,

My previous SessionHttpHandler had this in the ProcessRequest method to avoid redirection to the Login page when not authenticated.

However, when I add this line to ProcessRequest, I noticed that this line of code is not hit. Any ideas?

		public void ProcessRequest(HttpContext context)
		{
			// This flag must be set to true so that 401 Unauthorized errors are not automatically redirected to a login url
			context.Response.SuppressFormsAuthenticationRedirect = true;
			Handler.ProcessRequest(context);
		}

Implement the Async methods instead.

1 Like