ServiceStack returns 405 on OPTIONS request
Asked Answered
F

3

3

I'm building a REST webservice using ServiceStack. I want to allow cross-domain request, so I registered the CorsFeature plugin.

My AppHost looks as follows:

public class HomeAppHost : AppHostHttpListenerBase 
{
    public Context Context { get; set; }

    public HomeAppHost(Context context)
        : base("HomeAutomation", typeof(HomeInterfaceService).Assembly)
    {
        Context = context;
    }

    public override void Configure(Funq.Container container)
    {
        Plugins.Add(new CorsFeature());

        Routes
            .Add<HomeInterface>("/HomeInterface")
            .Add<HomeInterface>("/HomeInterface/{Id}")
            .Add<ViewModel>("/ViewModel")
            .Add<FunctionInput>("/Function")
        ;
    }
}

Then, when an OPTIONS request is made to the service, it results in a 405 Method Not Allowed:

Request:

OPTIONS /Function HTTP/1.1
Host: localhost:1337
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0 FirePHP/0.7.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: nl,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Origin: http://localhost
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
x-insight: activate
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

Response:

HTTP/1.1 405 Method Not Allowed
Content-Length: 1837
Content-Type: application/xml
Server: Microsoft-HTTPAPI/2.0
Date: Fri, 15 Feb 2013 20:19:33 GMT

Edit


Adding an empty Options method to the service does indeed prevent the 405 from being triggered. However, the response seems to be empty:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Server: Microsoft-HTTPAPI/2.0
Date: Sat, 16 Feb 2013 08:44:21 GMT

Adding the following also gives me an empty response:

RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
    //Handles Request and closes Responses after emitting global HTTP Headers
    if (httpReq.HttpMethod == "OPTIONS")
        httpRes.End();
});

I had to change httpReq.Method to httpReq.HttpMethod and httpRes.EndServiceStackRequest() to httpRes.End(). Is this correct?

Formal answered 15/2, 2013 at 20:49 Comment(1)
Possible duplicate of servicestack REST API and CORSDimidiate
F
2

Not sure whether this is the right way to go, but I'm now handling the CORS myself using a request filter:

RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
    httpRes.AddHeader("Access-Control-Allow-Origin", "*");

    if (httpReq.HttpMethod == "OPTIONS")
    {
        httpRes.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
        httpRes.AddHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
        httpRes.End();
    }
});
Formal answered 16/2, 2013 at 10:11 Comment(1)
Any idea how would the VB.net verions look like?Montymonument
E
2

405 in ServiceStack means that method has not been implemented.

So you need to add a handler for the Options verb. The method body can be empty, e.g:

public MyService : Service 
{ 
    public void Options(HomeInterface request) {}
}

If you wanted to allow all Options requests (i.e. regardless of which service it is), you can register a global request filter like:

this.RequestFilters.Add((httpReq, httpRes, requestDto) => {
   //Handles Request and closes Responses after emitting global HTTP Headers
    if (httpReq.Method == "OPTIONS") 
        httpRes.EndServiceStackRequest();
});

You can use the same logic in Filter Attributes if you want more fine-grained control over how Option requests are handled.

Edris answered 15/2, 2013 at 21:35 Comment(4)
The CorsFilter should be handling the CORS use of OPTIONS. So I'm not sure your answer is actually answering the question.Thought
The EnableCors Attribute only emits the specified CORS headers. It does not handle implicitly handle Options or any other HTTP verb.Edris
The headers don't just magically appear on their own. Have you registered Plugins.Add(new CorsFeature());? Because I don't see it. See this question on CORS for more details.Edris
VB.net version please?Montymonument
F
2

Not sure whether this is the right way to go, but I'm now handling the CORS myself using a request filter:

RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
    httpRes.AddHeader("Access-Control-Allow-Origin", "*");

    if (httpReq.HttpMethod == "OPTIONS")
    {
        httpRes.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
        httpRes.AddHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
        httpRes.End();
    }
});
Formal answered 16/2, 2013 at 10:11 Comment(1)
Any idea how would the VB.net verions look like?Montymonument
V
2

I was a bit confused about this behavior. Did not wanted to make dummy Options() methods on every service and add fake routes on every Dto class. All I've needed - that ServiceStack AppHost responsed for EVERY 'OPTIONS' request, on every url, with same behavior. So this is what I've ended with.

Created my own handler for Options:

public class OptionsRequestHandler : IHttpHandler, IServiceStackHttpHandler
{
    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        ProcessRequest(null, new HttpResponseWrapper(context.Response), null);          
    }

    public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
    {
        httpRes.EndServiceStackRequest();
        return;
    }
}

Then added it in host Configure method:

this.CatchAllHandlers.Add((httpMethod, pathInfo, filePath) =>
{
    if ("OPTIONS".Equals(httpMethod, System.StringComparison.InvariantCultureIgnoreCase))
        return new OptionsRequestHandler();
    else return null;
});

And surely did not forget the CorsFeature:

host.Plugins.Add(new ServiceStack.ServiceInterface.Cors.CorsFeature());

So this way ServiceStack responds with "200 OK" on every request with "OPTIONS" header, regardless of url, dto and service declarations.

Vino answered 30/4, 2013 at 10:50 Comment(4)
I've tried your approach and now, instead of 404, I am getting a 500 because of the "throw new System.NotImplementedException()". You don't get that?Vaud
no. looks like you've run into conditions, where this filter is called by original IHttpHandler routing, not the IServiceStackHttpHandler. maybe somewhere internal or etc. just change the first ProcessRequest() method contents to be like hereVino
@Vaud Modify content of ProcessRequest(HttpContext context) method - instead of throwing exception, add this: ProcessRequest(null, new HttpResponseWrapper(context.Response), null);Arguable
I've replaced that 'throw' block in code so it wont confuse anymore 8)Vino

© 2022 - 2024 — McMap. All rights reserved.