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?

Jim G.
  • 15,141
  • 22
  • 103
  • 166
sroes
  • 14,663
  • 1
  • 53
  • 72
  • Possible duplicate of [servicestack REST API and CORS](https://stackoverflow.com/questions/8211930/servicestack-rest-api-and-cors) – Jim G. Jul 05 '18 at 02:49

3 Answers3

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.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • The CorsFilter should be handling the CORS use of OPTIONS. So I'm not sure your answer is actually answering the question. – bmargulies Feb 15 '13 at 21:49
  • The [EnableCors Attribute](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/Cors/EnableCorsAttribute.cs) only emits the specified CORS headers. It does not handle implicitly handle **Options** or any other HTTP verb. – mythz Feb 15 '13 at 23:15
  • 1
    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](http://stackoverflow.com/a/8215777/85785) for more details. – mythz Feb 16 '13 at 14:22
  • VB.net version please? – Iladarsda Jul 19 '13 at 13:46
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();
    }
});
sroes
  • 14,663
  • 1
  • 53
  • 72
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.

justmara
  • 240
  • 2
  • 12
  • 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? – Dema Sep 24 '13 at 21:12
  • 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 [here](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/ServiceStackHttpHandler.cs) – justmara Sep 30 '13 at 08:36
  • @Dema Modify content of `ProcessRequest(HttpContext context)` method - instead of throwing exception, add this: `ProcessRequest(null, new HttpResponseWrapper(context.Response), null);` – eXavier Oct 31 '13 at 15:17
  • I've replaced that 'throw' block in code so it wont confuse anymore 8) – justmara Nov 14 '13 at 12:28