I built a RESTful service with ServiceStack which sends data to a database. I've tested it locally and it works great. When I deploy it to a server and run the same code, which is a jQuery $.ajax call, I get an 'Access is denied' error. I have setup CORS in my ServiceStack config using the plugin as explained here. I also set the crossDomain to true in my ajax call. I can't think of what else to do to get this to work, and I'm honestly not sure where this error is being thrown. I've stepped through the Javascript and it doesn't even get to the 'failure' block of the ajax call, the error is thrown before that... I am using IE9 to test, if that's pertinent...?
Any idea what might be going on?
Here's my ServiceStack POST method:
public CitationResponse Post(Citation citation)
{
var response = new CitationResponse { Accepted = false };
if (string.IsNullOrEmpty(citation.ReportNumber))
{
response.Accepted = false;
response.Message = "No data sent to service. Please enter data in first.";
return response;
}
try
{
response.ActivityId = Repository.CreateCitation(citation.ReportNumber, citation.ReportNumber_Prefix, citation.ViolationDateTime, citation.AgencyId, citation.Status);
response.Accepted = true;
}
catch (Exception ex)
{
response.Accepted = false;
response.Message = ex.Message;
response.RmsException = ex;
}
return response;
}
Here's my Javascript function which calls the web service:
SendCitationToDb: function(citation, callback) {
$.ajax({
type: "POST",
url: Citations.ServiceUrl + "/citations",
data: JSON.stringify(citation),
crossDomain: true,
contentType: "application/json",
dataType: "json",
success: function (data) {
if (!data.Accepted) {
Citations.ShowMessage('Citation not added', 'Citation not added to database. Error was: ' + data.Message, 'error');
} else {
citation.ActivityId = data.ActivityId;
callback(data);
}
},
failure: function(errMsg) {
Citations.ShowMessage('Citation not added', 'Citation not added to database. Error was: ' + errMsg.Message, 'error');
}
});
}
Thanks for your help!
Update: I just ran the same app in Chrome 29 and I get these errors (replaced real URLs for security):
OPTIONS http://servicedomain.com/citations Origin http://callingdomain.com is not allowed by Access-Control-Allow-Origin. XMLHttpRequest cannot load http://servicedomain.com//citations. Origin http://callingdomain.com is not allowed by Access-Control-Allow-Origin.
But I am clearly allowing all domains in my headers:
Plugins.Add(new CorsFeature()); //Enable CORS
SetConfig(new EndpointHostConfig {
DebugMode = true,
AllowJsonpRequests = true,
WriteErrorsToResponse = true,
GlobalResponseHeaders =
{
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" }
}
});
NOW, if I run the same service call through the REST Console app in Chrome I get a valid response from ServiceStack. Here's the response header:
Status Code: 200 Date: Fri, 20 Sep 2013 19:54:26 GMT Server: Microsoft-IIS/6.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET, ServiceStack/3.948 Win32NT/.NET Access-Control-Allow-Methods: POST,GET,OPTIONS, GET, POST, PUT, DELETE, OPTIONS Content-Type: application/json; charset=utf-8 Access-Control-Allow-Origin: *, * Cache-Control: private Content-Length: 58
So I'm totally lost as to why it works in a pure REST request, but not from the application??
Update:
After spending many hours trying many different solutions I've found online, my Configure method now looks like this:
public override void Configure(Container container)
{
SetConfig(new EndpointHostConfig
{
DefaultContentType = ContentType.Json,
ReturnsInnerException = true,
DebugMode = true, //Show StackTraces for easier debugging (default auto inferred by Debug/Release builds)
AllowJsonpRequests = true,
ServiceName = "SSD Citations Web Service",
WsdlServiceNamespace = "http://www.servicestack.net/types",
WriteErrorsToResponse = true,
GlobalResponseHeaders =
{
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" }
}
});
container.RegisterAutoWired<Citation>();
container.RegisterAutoWired<Driver>();
container.RegisterAutoWired<Vehicle>();
container.RegisterAutoWired<Violations>();
using (var getAttributes = container.Resolve<AttributesService>())
getAttributes.Get(new AttributesQuery());
Plugins.Add(new CorsFeature());
RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
httpRes.AddHeader("Access-Control-Allow-Origin", "*");
httpRes.AddHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
httpRes.AddHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
if (httpReq.HttpMethod == "OPTIONS")
httpRes.EndServiceStackRequest(); // extension method
});
Routes
.Add<Attribute>("/attributes", "GET, OPTIONS")
.Add<Citation>("/citations", "POST, GET, OPTIONS, DELETE")
.Add<Driver>("/driver", "POST, OPTIONS")
.Add<Vehicle>("/vehicle", "POST, OPTIONS")
.Add<Violations>("/violations", "POST, OPTIONS");
var config = new AppConfig(new ConfigurationResourceManager());
container.Register(config);
}
}
At this point I'm not sure what to do. I've tried everything, but am still getting the same errors. The methods continue to work fine using the REST Console in Chrome, which is a little infuriating, since I can never get them to work calling them from a web page. I'm almost ready to re-write the whole thing in WCF, but I really would love to get the ServiceStack version working, since I know it works locally! If anyone has any other suggestions I can try, I would SO appreciate your help!
Update: See comment at the bottom for details. I had to remove headers from the HTTP Headers tab in IIS. Not sure when I put those in, but for anyone else who might face the same issue, here's a screenshot of the tab in IIS: