I have a WCF service that I need to run over SSL, I am calling it from a webpage (using jQuery) which may or may not be a secure page. The problem is, if I make the call from a secure webpage on my site, the call runs exactly how I would expect...however, if I make the call from a non-secure page on my site, to the secure web service (using "https://" ;) ) it returns null data (via Firebug). Anything I'm missing? Is this even possible?
Here is the configuration of the service I'm calling (I'm more than happy to provide more stuff if needed):
<behaviors>
<endpointBehaviors>
<behavior name="AspNetAjaxBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="ClientServices.Membership" behaviorConfiguration="ServiceGatewayBehavior">
<endpoint address="" behaviorConfiguration="AspNetAjaxBehavior" bindingConfiguration="SecureBinding"
binding="webHttpBinding" contract="ClientServices.Membership" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="SecureBinding">
<security mode="Transport"/>
</binding>
</webHttpBinding>
</bindings>
Here is the code that calls the service:
$.ajax({
url: serviceUrl,
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: '{"review":{"Username":"' + username + '"}}',
success: function (data) {
$.log(data);
},
error: function (a, b, c) {
$.log(b);
},
cache: false
});
UPDATE
If I change the service call method to "GET" and call it directly over SSL it works fine and outputs the jSon that I would expect. It's only inside the non-secure page where the problem persists.
Assuming both site and service are both at yourdomain.com, then my first thoughts would be that when on the non-secure page, your browser is not attaching the cookie it has for the site domain and so when it calls the service, it isn't authenticated.
Have you used Fiddler to look at the raw HTTP header data and more importantly, what differs in this data when on the working and not-working page?
Luke
Related
Is it possible to handle "Cross Origin Resource Sharing" requests for a WCF service while enforcing Windows Authentication?
My scenario:
I have set up a self hosted WCF service exposed through a webHttpBinding.
This service is supposed to be called directly from the browser using jQuery. Practically speaking, this would limit me to using either the basicHttpBinding or the webHttpBinding. In this case, I'm using the webHttpBinding for calling the service operations.
The HTML pages (that will call the WCF service) are served from a web-server on the same machine but on a different port than the WCF service. This means I'll need CORS support to get this working in Firefox, Chrome, ...
Users must authenticate using Windows authentication when calling the WCF service. To this end, I have configured my webHttpBinding to use the transport security mode "TransportCredentialsOnly".
The W3C dictates that CORS should be used in such cases.
Simply stated, this means that the browser will detect that I am doing a cross-domain request. Before actually sending the request to my WCF service, it will send a so-called "preflight" request to my WCF service URL. This preflight request uses the HTTP method "OPTIONS" and asks whether the originating URL (= the webserver that served my HTML) is allowed to send the request to my service URL. The browser then expects an HTTP 200 response (= "OK") before sending the actual request to my WCF service. Any other reply from my service will prevent the actual request from being sent.
CORS is not built into WCF at this time, so I've used WCF extension points to add CORS compatibility.
The services section of the App.Config for my self-hosted service:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
I have implemented an IDispatchMessageInspector that replies to preflight messages:
public class CORSSupport : IDispatchMessageInspector
{
private Dictionary<string, string> requiredHeaders;
public CORSSupport(Dictionary<string, string> requiredHeaders)
{
this.requiredHeaders = requiredHeaders ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequest = request.Properties["httpRequest"] as HttpRequestMessageProperty;
if (httpRequest.Method.ToUpper() == "OPTIONS")
instanceContext.Abort();
return httpRequest;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequest = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (KeyValuePair<string, string> item in this.requiredHeaders)
httpResponse.Headers.Add(item.Key, item.Value);
string origin = httpRequest.Headers["origin"];
if (origin != null)
httpResponse.Headers.Add("Access-Control-Allow-Origin", origin);
if (httpRequest.Method.ToUpper() == "OPTIONS")
httpResponse.StatusCode = HttpStatusCode.NoContent;
}
}
This IDispatchMessageInspector is registered through a custom IServiceBehavior attribute.
I call my service through jQuery like so:
$.ajax(
{
url: 'http://localhost/Temporary_Listen_Addresses/myapp/LookupService/SomeLookup',
type: 'GET',
xhrFields:
{
withCredentials: true
}
}
)
.done(function () { alert('Yay!'); })
.error(function () { alert('Nay!'); });
This works in IE10 and Chrome (I get a message box saying "Yay!"), but not in Firefox. In Firefox, I get a "Nay!" and a HTTP 401 (unauthorized) error.
This 401 is due to the "Windows Authentication" that I have set up in my service configuration. The way authentication works is the browser first sends a request without any authentication info. The server then replies back with HTTP 401 (unauthorized) indicating the authentication method to use. The browser would then normally re-submit the request including the user-credentials (after which the request would proceed normally).
Unfortunately, it seems the W3C has indicated that credentials should not be passed into the CORS preflight messages. Hence, WCF replies back with a HTTP 401. It seems that Chrome somehow does send the credentials in the preflight request header (which is actually incorrect, according to W3C specs), while Firefox does not.
Furthermore, the W3C recognizes only the HTTP 200 response to a preflight requests: any other response (such as the HTTP 401 I receive) simply means the CORS request failed and the actual request may not be submitted...
I don't know how to get this (simple) scenario working. Can anyone help?
Gotten a bit further.
With .NET 4.5, it is possible to support multiple authentication schemes for a single endpoint. This allowed me to define both Windows authentication and anonymous authentication simultaneously:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
<serviceAuthenticationManager authenticationSchemes="Negotiate, Anonymous"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="InheritedFromHost"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
This way, my IDispatchMessageInspector does get called and I can properly handle the preflight messages for all browsers.
I then wanted to adapt my IDispatchMessageInspector to enforce authentication for any request other than the preflights:
public class CrossOriginResourceSharingMessageInspector : IDispatchMessageInspector
{
private Dictionary<string, string> requiredHeaders;
public CrossOriginResourceSharingMessageInspector(Dictionary<string, string> requiredHeaders)
{
this.requiredHeaders = requiredHeaders ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequestHeader = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
if (httpRequestHeader.Method.ToUpper() == "OPTIONS")
instanceContext.Abort();
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
instanceContext.Abort();
return httpRequestHeader;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequestHeader = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponseHeader = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
foreach (KeyValuePair<string, string> item in this.requiredHeaders)
httpResponseHeader.Headers.Add(item.Key, item.Value);
string origin = httpRequestHeader.Headers["origin"];
if (origin != null)
httpResponseHeader.Headers.Add("Access-Control-Allow-Origin", origin);
string method = httpRequestHeader.Method;
if (method.ToUpper() == "OPTIONS")
{
httpResponseHeader.StatusCode = HttpStatusCode.NoContent;
}
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
httpResponseHeader.StatusDescription = "Unauthorized";
httpResponseHeader.StatusCode = HttpStatusCode.Unauthorized;
}
}
}
Again, this seems to work for IE and Chrome, but not for Firefox. Preflight is now ok for Firefox, but it seems Firefox is not re-submitting the request after I replied with a HTTP 401 when the actual request didn't contain user credentials. In fact, I would expect Firefox to send the credentials along with the GET request immediately (seeing as I added "withCredentials: true" in my jQuery AJAX request; Chrome does seem to do this correctly, though).
What am I doing wrong?
Eureka (kind of). It seems that Firefox didn't like the "Negotiate" authentication I specified for my service. It seems to work when I change the authentication scheme from "Negotiate, Anonymous" to "Ntlm, Anonymous":
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyApp.DefaultServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
<serviceAuthenticationManager authenticationSchemes="Ntlm, Anonymous"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyApp.DefaultEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="MyApp.DefaultWebHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="InheritedFromHost"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service
name="MyApp.FacadeLayer.LookupFacade"
behaviorConfiguration="MyApp.DefaultServiceBehavior"
>
<endpoint
contract="MyApp.Services.ILookupService"
binding="webHttpBinding"
bindingConfiguration="MyApp.DefaultWebHttpBinding"
address=""
behaviorConfiguration="MyApp.DefaultEndpointBehavior"
>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/Temporary_Listen_Addresses/myapp/LookupService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
I thought Firefox supported the "Negotiate" scheme... Anyone has any idea why it didn't work?
Long story short:
My WCF clients should be able to provide both username and certificate to a service hosted in IIS, where I should use that information to validate requests using a custom policies.
Complete story:
I have the need to authenticate some WCF clients to verify if they can execute operations.
We have two kinds of clients: WPF applications and a web application. We would like to do the following:
The web application uses a certificate trusted by the service so that it is recognized as a special user with all permissions (the web application already verifies permissions by itself and we wouldn't like to touch it by now)
The WPF clients authenticate themselves with username/password provided by the user
In the implementation of the operations, I would like to verify if the certificate was provided (then I recognize the "super user"), otherwise fallback to username/password authentication.
Services are hosted in IIS 7 and we need to use NetTcpBinding.
I was able to implement the username validation, but the problem is that the AuthorizationContext inspected by the service contains only identity information, and not the certificate.
The following code is used on the client side to initialize the creation of channels (from a spike I'm using to test the solution):
var factory = new ChannelFactory<T>(this.Binding, address);
var defaultCredentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
factory.Endpoint.Behaviors.Remove(defaultCredentials);
var loginCredentials = new ClientCredentials();
loginCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.None;
loginCredentials.UserName.UserName = username;
loginCredentials.UserName.Password = password;
if (useCertificate)
{
loginCredentials.SetCertificate();
}
factory.Endpoint.Behaviors.Add(loginCredentials);
return factory.CreateChannel();
With the SetCertificate extension being implemented like this:
public static void SetCertificate(this ClientCredentials loginCredentials)
{
loginCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "SecureWcfClient");
}
This is the configuration of the web application hosting the services:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="Test"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<clientCertificate>
<authentication certificateValidationMode="Custom" customCertificateValidatorType="AuthenticationProtectedService.Security.CertificateValidator, AuthenticationProtectedService.Security"/>
</clientCertificate>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="AuthenticationProtectedService.Security.UserNamePassValidator, AuthenticationProtectedService.Security" />
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="AuthenticationProtectedService.Security.CertificateAuthorizationManager, AuthenticationProtectedService.Security"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding>
<security mode="None"/>
</binding>
<binding name="SecureNetTcp">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</netTcpBinding>
</bindings>
<service
name="AuthenticationProtectedService.Services.OneWayServiceB"
behaviorConfiguration="SecureBehavior">
<endpoint
address=""
binding="wsHttpBinding"
contract="AuthenticationProtectedService.ServiceModel.IOneWayServiceB">
</endpoint>
</service>
<service
name="AuthenticationProtectedService.Services.DuplexServiceB" behaviorConfiguration="SecureBehavior">
<endpoint
address=""
binding="netTcpBinding"
bindingConfiguration="SecureNetTcp"
contract="AuthenticationProtectedService.ServiceModel.IDuplexServiceB">
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
Finally, this is the implementation of the custom authorization manager (I also tried with a custom certificate validator but the function was never run)
public class CertificateAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
if (!base.CheckAccessCore(operationContext))
{
return false;
}
string thumbprint = GetCertificateThumbprint(operationContext);
// I'd need to verify the thumbprint, but it is always null
return true;
}
private string GetCertificateThumbprint(OperationContext operationContext)
{
foreach (var claimSet in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
foreach (Claim claim in claimSet.FindClaims(ClaimTypes.Thumbprint, Rights.Identity))
{
string tb = BitConverter.ToString((byte[])claim.Resource);
tb = tb.Replace("-", "");
return tb;
}
}
return null;
}
}
I think that the problem could be in the clientCredentialType property of the nettcpbinding.Security.Message node on the service configuration, but I don't see the option to use both Certificate and Username withing the Message security.
Any help appreciated, thanks
Remark: a specific goal of the project is to have very low level impact on server setup and in general in the system, so also SSL should be avoided if possible.
try out this link http://msdn.microsoft.com/en-us/library/ms733099.aspx ...it might resolve your issue where in you can have different binding configuration for same binding type and associate the same to different endpoints as per your need.
I have two projects, one is WCF Service, which is to speak a text/sentence in a text box.
public class Service1 : IService1
{
public string RunTts(string text)
{
using (SpeechSynthesizer synth = new SpeechSynthesizer())
{
// Configure the audio output.
synth.SetOutputToDefaultAudioDevice();
synth.Speak(text);
return "";
}
}
}
Then I call it with ajax in the _Layout.cshtml page in the second project, which is asp.net mvc.
<script type="text/javascript">
function ttsFunction() {
serviceUrl = "Service1.svc/RunTts";
$.ajax({
type: "POST",
url: serviceUrl,
data: '{"text": "' + $('#speak').val() + '"}',
contentType: "text/xml; charset=utf-8",
dataType: "text/xml",
error: function (xhr,status,error) {
console.log("Status: " + status); // got "error"
console.log("Error: " + error); // got "Not Found"
console.log("xhr: " + xhr.readyState); // got "4"
},
statusCode: {
404: function() {
console.log("page not found"); // got
}
}
});
}
</script>
Because I got 404 error, so I think the url is wrong. Please see the structure of files, the web reference is called 'ServiceReference1' I guess.
As shown in your screenshot, the service is not hosted in your web application. You cannot access such a service (hosted outside of your web application) directly from the client side, because you're violating the same origin policy restriction. It's one of the underlying concepts of trust, on which web security is based on (e.g. protection aganist XSS) - you cannot send cross domain AJAX requests. This essentially states that if content from one site (e.g. https://bank.ny.com) is granted permission to access resources on the system, then any content from that site will share these permissions, while content from another site (https://nsa.ny.com) will have to be granted permissions separately (in general, the term origin is defined using the domain name, application layer protocol, and port number).
Nevertheless, you have at least 4 solutions to solve your problem:
First - talk to your service through the middle-controller layer. Going this way implies to have proxy class generated (by svcutil.exe, what you have done by adding service reference using Visual Studio). Communication with this client looks like below:
public class TtsController
{
public JsonResult RunTts(string text)
{
using(var client = new ServiceReference1.Service1Client())
{
var response = client.RunTts(text);
return Json(response);
...
The JavaScript side should then use such an URL: var serviceUrl = "/Tts/RunTts" (along with proper JSON data passing to the AJAX request, which I'll go through a bit further).
Second - talk directly to the service. If you want to communicate directly with the service, you have to host this service in your web application. The correct WCF configuration should be followed to support RESTful services:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webby">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Namespace.Service1">
<endpoint address=""
behaviorConfiguration="webby"
binding="webHttpBinding"
contract="Namespace.IService1" />
</service>
</services>
</system.serviceModel>
For a RESTful endpoint, the binding you should use is WebHttpBinding along with appropriate behavior. Alternatively there is configuration-free experience for many RESTful services - WebServiceHostFactory. Your .svc file should look like below (MSDN):
<%# ServiceHost Language="C#" Debug="true" Service="Namespace.Service1"
CodeBehind="Service1.svc.cs"
Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
WebServiceHostFactory creates an instance of the WebServiceHost, and since the WebServiceHost will auto-configure the endpoint using WebHttpBinding and related behavior, there doesn't need to be any configuration for this endpoint in the web.config at all (of course, if you need to customize the binding, you have to use the configuration) (MSDN).
Then to access the service use appropriate full URL: http://localhost:[port]/Service1.svc/RunTts or relative one: /Service1.svc/RunTts.
Since you're using ASP.NET MVC, based on your routes definitions, the request will dispatched to some controller, where such an action doesn't exist. You have to tell MVC to ignore route to your service:
routes.IgnoreRoute("{resource}.svc/{*pathInfo}");
(BTW: If you put your .svc file under different directory within your application, modify respectively URL and route to ignore.)
Your code needs some additional fixes:
If you want to send message in JSON format, specify dataType and contentType parameters correctly:
$.ajax({
url: serviceUrl,
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
...
Do not construct your JSON strings manually, as it can lead to further parsing errors - use converters e.g.:
var data = new Object();
data.text = $('#speak').val();
var jsonString = JSON.stringify(data);
$.ajax({
...
data: jsonString,
...
Provide additional declarative information to your service:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string RunTts(string text);
...
Remove service reference from the project. You don't need it as there is no usage of middle-controller here.
Third - JSONP (look here and here) can be used to overcome origin policy restriction. But you can't POST using JSONP because it just doesn't work that way - it creates a <script> element to fetch data, which has to be done via GET request. JSONP solution doesn't use XmlHttpRequest object, so it is not an AJAX request in the standard way of understanding, but the content is still accessed dynamically - no difference for the end user.
$.ajax({
url: serviceUrl,
dataType: "jsonp",
contentType: "application/json; charset=utf-8",
data: data,
...
[OperationContract]
[WebGet(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate="RunTts?text={text}")]
public string RunTts(string text);
RESTful WCF configuration with cross domain requests allowed:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="jsonp" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webby">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Namespace.Service1">
<endpoint address=""
behaviorConfiguration="webby"
binding="webHttpBinding"
bindingConfiguration="jsonp"
contract="Namespace.IService1" />
</service>
</services>
</system.serviceModel>
Fourth - CORS. Implemented in modern browsers alternative to JSON with Padding.
i've built a WCF web application , exposed it's method into get enabled methods
[OperationContract]
[WebGet]
string getStatistics();
[OperationContract]
[WebGet]
string getVenues(string BrandName, int limit);
and edited the config file :
<endpoint address="json" binding="webHttpBinding" contract="foursquare2RDF.IVenue2rdf" behaviorConfiguration="restBehavior"/>
and in the service behavior :
<endpointBehaviors>
<behavior name="restBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
i hosted the service on the IIS , and it works very fine from the browser so when u hit :
http://localhost:83/venue2rdf.svc/json/getStatistics
it returns a good results
the problem is i can't consume this restful service from if shows those errors :
OPTIONS http://localhost:83/venue2rdf.svc/json/getStatistics?{'venues':'100'} 405 (Method Not Allowed)
XMLHttpRequest cannot load [http://localhost:83/venue2rdf.svc/json/getStatistics][1]. Origin null is not allowed by Access-Control-Allow-Origin.
i'm using that code to call the service :
$.ajax({
type: "get",
url: statisticsURL,
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
eval("var x = " + msg.d);
console.log(x);
}
});
what ive reached so far :
i tried replacing $.ajax with $.getjson like stated in similar question
and the error 405 was removed , the second error just appears
i've found something called Ajax enabled WCF service project , but still i don't want to migrate in to a new project
i know there are similar questions but all don't fit , showing different errors that mine
You should probably make it a JSONP request since your going cross domain, you running into the same origin policy:
$.getJSON(stastatisticsURL + "?callback=?", success: function (msg) {
eval("var x = " + msg.d);
console.log(x);
});
the ?callback=? part tels jquery to make it JSONP. I advise you to read up on what JSONP is since it isn't a silver bullet. To enable JSONP on WCF services read:
C# WCF Web API + JSONP
For you to consume a cross domain WCF REST service using jQuery please find a sample below:
My Service looks as below:
[ServiceContract]
public interface IJSONPService
{
[OperationContract]
[WebGet]
string GetDate();
[OperationContract]
[WebInvoke]
string PostData(string name);
}
Now my config entries for the above service looks as shown:
<services>
<service name="Service.JSONPService">
<endpoint address="" binding="webHttpBinding" behaviorConfiguration="json" bindingConfiguration="defaultRestJsonp" contract="Service.IJSONPService">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="json">
<enableWebScript />
</behavior>
</behaviors>
</endpointBehaviors>
<webHttpBinding>
<binding name="defaultRestJsonp" crossDomainScriptAccessEnabled="true">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxDepth="64" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</webHttpBinding>
You need to note the crossDomainScriptAccessEnabled attribute in the binding element "defaultRestJsonp" which takes care of determining the request to be for JSONP and appropriately converting the response to be wrapped in the callback method from the URL which comes as a query string
Now from your page do the below JavaScript that calls the above WCF REST service as shown:
function TestingWCFRestWithJsonp() {
$.ajax({
url: "http://domain.com/Service/JSONPService.svc/GetDate",
dataType: "jsonp",
type: "GET",
timeout: 10000,
jsonpCallback: "MyCallback",
success: function (data, textStatus, jqXHR) {
alert(data);
},
error: function (jqXHR, textStatus, errorThrown) {alert('error');
},
complete: function (jqXHR, textStatus) {alert('complete');
}
});
}
function MyCallback(data) {
alert(data);
}
Check out the jsonpCallback property in the $.ajax method call.
The raw request to the web service call looks as below:
GET http://localhost/Service/JSONPService.svc/GetDate?callback=MyCallback&_=1343391683779 HTTP/1.1
Host: localhost
Connection: keep-alive
And the raw response from the WCF REST service looks as below:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/x-javascript
Date: Fri, 27 Jul 2012 12:21:23 GMT
Content-Length: 27
MyCallback("27\/07\/2012");
NOTE: When you perform a JSONP request your $.ajax methods error/complete/success are not called.
I want to make a web service with the following properties:
It uses WCF and .NET 4.0
It is hosted in IIS7
It is RESTful
It's okay to keep the default output behaviour of collecting and handling WebFaultExceptions etc
It has a single call that
eats naked HTTP POST of potentially huge binary files (should preferably not be kept in memory!)
accepts a Stream as an input
outputs a Stream
uses an UriTemplate for matching (there will be more calls soon)
wants the streams to be completely raw and NOT have IIS or WCF try to be smart by handling the content type in any way
The problem is that IIS and/or WCF keep interfering regarding the Content-Type, insisting on returning
415 Cannot process the message because the content type '...' was not the expected type 'text/xml; charset=utf-8'
no matter what the content type was. Can you spot any errors I have made below?
[ServiceContract]
public interface IRenderService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/render", BodyStyle = WebMessageBodyStyle.Bare)]
Stream Render(Stream input);
}
With the following snippets from Web.config:
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime maxRequestLength="500000000" />
</system.web>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding
name="FileStreamConfiguration"
transferMode="Streamed"
maxReceivedMessageSize="500000000"
maxBufferSize="500000000"
openTimeout="00:25:00"
closeTimeout="00:25:00"
sendTimeout="00:25:00"
receiveTimeout="00:25:00" />
</webHttpBinding>
</bindings>
<services>
<service name="RenderService" behaviorConfiguration="RenderServiceBehavior">
<endpoint address="" binding="webHttpBinding" contract="RenderServer.IRenderService" bindingConfiguration="FileStreamConfiguration" behaviorConfiguration="RenderEndpointBehaviour" >
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RenderServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="RenderEndpointBehaviour">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
I want to always get the raw contents of the HTTP POST body, and fetch headers from WebOperationContext.Current.IncomingRequest manually if I deem that necessary, and IIS/WCF should completely ignore all aspects of the request besides parsing it and sending it to my code. I'll use WebOperationContext.Current.OutgoingResponse to set aspects of the output as I see fit, also manually.
This is so easy to do with the new WCF Web API library. See http://wcf.codeplex.com I have a sample on my blog which I will post once the power comes back on :-)
The interface looks like this,
[ServiceContract]
public interface IRenderService{
[WebInvoke(Method = "POST", UriTemplate = "/render")]
HttpResponseMessage Render(HttpRequestMessage input);
}