I'm trying to install a managed windows service-hosted WCF service that can give my webpage some information about the machine it is installed on. The idea is all of the visitors to my page will have this service installed and the page can use javascript to call the localhost service to get the data its after.
One of our developers put this together by enabling CORS for the service using
CarlosFigueira's example here. It works great in all browsers except for in Edge, it gives the error SCRIPT7002: XMLHttpRequest: Network Error 0x2efd
. I can't think of any reason for this and couldn't find any information on it, so I thought maybe I should try a jsonp approach instead.
I thought jsonp was supposed to bypass the same-origin policy, but I've only been able to get it to work** with the above CORS-enabled WCF service stuff still in place. As soon as I remove that and just use a regular ServiceHost
, I start getting a 400 - Bad request response.
**By "work" I mean the browser successfully makes the request and receives a response. However, the response is plain json, not jsonp. I read that WCF 4.5 is supposed to automatically recognize the "callback" parameter and wrap the json in the "P" but that doesn't seem to be happening. I think this is an unrelated, secondary problem.
But the main issue is, I thought jsonp was supposed to be a way of making cross-domain requests so why do I have to enable all the CORS header stuff on my WCF service to make it work?
Windows Service OnStart:
CorsEnabledServiceHostFactory serviceHostFactory = new CorsEnabledServiceHostFactory();
serviceHost = serviceHostFactory.GetServiceHost(typeof(LPA.MachineDataService), baseAddresses); //Works
//LPA.MachineDataService singleton = new LPA.MachineDataService();
//serviceHost = new System.ServiceModel.ServiceHost(singleton, baseAddresses); //Doesn't work
serviceHost.Open();
ServiceContract:
[ServiceContract]
public interface IMachineDataService
{
[WebGet(UriTemplate = "GetValues", ResponseFormat=WebMessageFormat.Json)]
LPA.MachineData GetValues();
}
Service Implementation:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MachineDataService : IMachineDataService
{
public LPA.MachineData GetValues()
{
LPA.MachineData result = null;
try
{
WebOperationContext.Current.OutgoingResponse.Headers[HttpResponseHeader.CacheControl] = "no-cache";
result = PcValues.GetValues();
}
catch (Exception ex)
{
}
return result;
}
}
From windows service app.config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<standardEndpoints>
<webScriptEndpoint>
<standardEndpoint crossDomainScriptAccessEnabled="true" name=""></standardEndpoint>
</webScriptEndpoint>
<webHttpEndpoint>
<standardEndpoint crossDomainScriptAccessEnabled="true" name=""></standardEndpoint>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
jQuery Call:
var valuesAddress = "http://localhost:56731/ValuesService/GetValues";
$.ajax({
type: "GET",
url: valuesAddress,
dataType: "jsonp",
success: function (result) {
$("[data-authinfo]").val(result);
},
error: function (xhr, status, code) {
}
});
I started a self-hosted dummy service from scratch and narrowed down the problem to my configuration. It needs to be using a webHttpBinding
to function as a REST service and I guess, without explicitly specifying so, it was instead using a basicHttpBinding
. I removed the standard endpoints and configured the endpoint myself to this:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="webHttpBinding" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings>
<services>
<service name="LessonPlansAuthentication.MachineDataService">
<endpoint address="" binding="webHttpBinding"
bindingConfiguration="webHttpBinding"
behaviorConfiguration="webHttpBehavior"
contract="LessonPlansAuthentication.IMachineDataService"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
</system.serviceModel>
If I tried to visit the URL in the browser before making this change, it would just display a blank white page. After, it actually returns the JSON data and jQuery can successfully do a JSONP AJAX call to the URL.
I'm still unclear exactly what using the webHttp binding & endpoint along with crossDomainScriptAccessEnabled=true
actually changes to allow the call. Content type of the response maybe? Any comments on that would be appreciated.
Now I just have to verify it works in all browsers. If anyone knows why Edge is not supporting the service with CORS enabled, please leave a comment.