Question

Edit:

The error appears to be related to using location in web.config.

I was able to get the workaround working in a completely new project, however, once I specified a specific location for the API, I started seeing the error again.


I have an existing ASP.NET web app that I'm adding Service Stack to. The app relies heavily on ASP.NET Session. Unfortunately I am unable to access sessions in my Service Stack classes as HttpContext.Current.Session is always null.

So I've implemented a VB.NET version of a workaround from this Stack Overflow answer, however when I navigate to the URL Service Stack is configured on, I receive the following error:

Handler for Request not found: 

Request.ApplicationPath: /
Request.CurrentExecutionFilePath: /api/
Request.FilePath: /api/
Request.HttpMethod: GET
Request.MapPath('~'): D:\Hg\MyApp.Web\
Request.Path: /api/
Request.PathInfo: 
Request.ResolvedPathInfo: /api
Request.PhysicalPath: D:\Hg\MyApp.Web\api\
Request.PhysicalApplicationPath: D:\Hg\MyApp.Web\
Request.QueryString: 
Request.RawUrl: /api/
Request.Url.AbsoluteUri: http://localhost:28858/api/
Request.Url.AbsolutePath: /api/
Request.Url.Fragment: 
Request.Url.Host: localhost
Request.Url.LocalPath: /api/
Request.Url.Port: 28858
Request.Url.Query: 
Request.Url.Scheme: http
Request.Url.Segments: System.String[]
App.IsIntegratedPipeline: True
App.WebHostPhysicalPath: D:\Hg\MyApp.Web
App.WebHostRootFileNames: [aspnetemail.xml.lic,componentart.uiframework.lic,debug.aspx,debug.aspx.designer.vb,debug.aspx.vb,default.aspx,default.aspx.designer.vb,default.aspx.vb,favicon.ico,global.asax,global.asax.vb,licenses.licx,login.aspx,login.aspx.designer.vb,login.aspx.vb,logout.aspx,logout.aspx.designer.vb,logout.aspx.vb,packages.config,MyApp.web.vbproj,MyApp.web.vbproj.user,readme.txt,robots.txt,switch.aspx,switch.aspx.designer.vb,switch.aspx.vb,web.config,web.debug.config,web.release.config,_app_offline.htm,api,apps,app_code,app_data,app_start,bin,content,error,frameset,homepages,my project,obj,_clientdata,_system]
App.DefaultHandler: DefaultHttpHandler
App.DebugLastHandlerArgs: GET|/api/|D:\Hg\MyApp.Web\api\

Everything works fine (except Sessions) when I use the default Service Stack HTTP handler. When I swap in SessionHttpHandlerFactory, I get the above error.

According to the instructions on the workaround, you have to change the type attribute from ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack to SomeNamespace.SessionHttpHandlerFactory:

Web.Config:

<location path="api">
        <system.web>
            <authorization>
                <allow users="*" />
            </authorization>
            <httpHandlers>
                <add path="*" type="MyApp.Web.SessionHttpHandlerFactory" verb="*" />
                <!--<add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />-->
            </httpHandlers>
        </system.web>

        <!-- Required for IIS7 -->
        <system.webServer>
            <modules runAllManagedModulesForAllRequests="true"/>
            <validation validateIntegratedModeConfiguration="false" />
            <handlers>
                <add path="*" name="ServiceStack.Factory" type="MyApp.Web.SessionHttpHandlerFactory" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />
                <!--<add path="*" name="ServiceStack.Factory" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />-->
            </handlers>
        </system.webServer>
    </location>

Here are the two classes the workaround uses, converted from C# into VB.NET:

SessionHandlerDecorator.vb:

Public Class SessionHandlerDecorator
    Implements IHttpHandler
    Implements IRequiresSessionState

    Private Property Handler() As IHttpHandler
        Get
            Return m_Handler
        End Get
        Set(value As IHttpHandler)
            m_Handler = value
        End Set
    End Property
    Private m_Handler As IHttpHandler

    Friend Sub New(handler As IHttpHandler)
        Me.Handler = handler
    End Sub

    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return Handler.IsReusable
        End Get
    End Property

    Public Sub ProcessRequest(context As HttpContext) Implements IHttpHandler.ProcessRequest
        Handler.ProcessRequest(context)
    End Sub
End Class

SessionHttpHandlerFactory:

Imports ServiceStack.WebHost.Endpoints

Public Class SessionHttpHandlerFactory
    Implements IHttpHandlerFactory

    Private Shared ReadOnly factory As New ServiceStackHttpHandlerFactory()

    Public Function GetHandler(context As HttpContext, requestType As String, url As String, pathTranslated As String) As IHttpHandler Implements IHttpHandlerFactory.GetHandler
        Dim handler = factory.GetHandler(context, requestType, url, pathTranslated)
        Return If(handler Is Nothing, Nothing, New SessionHandlerDecorator(handler))
    End Function

    Public Sub ReleaseHandler(handler As IHttpHandler) Implements IHttpHandlerFactory.ReleaseHandler
        factory.ReleaseHandler(handler)
    End Sub

End Class

Here is my Service Stack implementation. Like I said, it works perfectly using the default Service Stack HTTP handler.

AppHost.vb:

Imports ServiceStack.WebHost.Endpoints

<Assembly: WebActivator.PreApplicationStartMethod(GetType(LoginAppHost), "Start")> 

Public Class LoginAppHost
    Inherits AppHostBase

    Public Sub New()
        'Tell ServiceStack the name and where to find your web services
        MyBase.New("My Happy Login API", GetType(LoginService).Assembly)
    End Sub

    Public Overrides Sub Configure(container As Funq.Container)
        'Set JSON web services to return idiomatic JSON camelCase properties
        ServiceStack.Text.JsConfig.EmitCamelCaseNames = True

    End Sub

    Public Shared Sub Start()
        Dim app As New LoginAppHost
        app.Init()
    End Sub
End Class

Login.vb:

Imports ServiceStack.ServiceInterface
Imports ServiceStack.ServiceHost

Public Class LoginService
    Inherits Service

    Public Function Any(request As SSOLogin) As Object

        Return New SSOLoginResponse With {.Valid = True, .RedirectUri = "http://localhost:28858/Iloveturtles.aspx"}

    End Function

End Class

<Route("/login")> _
Public Class SSOLogin
    Public Property UserName As String
    Public Property Key As String
    Public Property Redirect As String
End Class

Public Class SSOLoginResponse
    Public Property Valid As Boolean
    Public Property RedirectUri As String
End Class

I am at a complete loss on how to proceed. Any thoughts?

Was it helpful?

Solution

Does adding this to the LoginAppHost Configure method correct your issue?

Public Overrides Sub Configure(container As Funq.Container)
    'Set JSON web services to return idiomatic JSON camelCase properties
    ServiceStack.Text.JsConfig.EmitCamelCaseNames = True

    'code in the config options
    SetConfig(New EndpointHostConfig With
              {
                  .ServiceStackHandlerFactoryPath = "api", 'path where ServiceStack is hosted
                  .MetadataRedirectPath = "api/metadata" 'redirect path for /api to show meta data
              })
End Sub

I'm not quite sure why the 'config' elements wouldn't get picked up from the web.config file but this should explicitly handle the location/path issue. Also, not sure about your environment but this should work using Visual Studio 2012/IISExpress with the code/configs in your example.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top