Calling an AJAX Enabled WCF Service from jQuery - wcf

I am developing a mobile application using PhoneGap and jQuery Mobile. My aim is to create a web service which will enable the client (mobile) to query against a database.
After some research, I found out that AJAX Enabled Services might be what I was looking for.
So, I began by creating an AJAX-Enabled WCF Service and for now I added only the following method:
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json)]
public string GetString()
{
return "Hello there";
}
My web.config looks like this:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="WebApplication1.MobileServiceAspNetAjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<services>
<service name="WebApplication1.MobileService">
<endpoint address="" behaviorConfiguration="WebApplication1.MobileServiceAspNetAjaxBehavior"
binding="webHttpBinding" contract="WebApplication1.MobileService" />
</service>
</services>
</system.serviceModel>
</configuration>
After finishing off this service, I called from the client using the following method:
$.ajax({
type: "POST",
url: "http://localhost:11634/MobileService.svc/GetString",
contentType: "application/json",
data: "{}",
dataType: "json",
success: function (result) {
$("#textbox").text(result);
},
error: function (textStatus) {
alert(textStatus);
}
});
When calling the service, I am getting the following error [object Object]. Can you guide me on what am I doing wrong and whether I am using the right technologies please?

As Tariqulazam rightly points out [Object object] is not an error but a response object. To access the data you could modify your code to read:
success: function (result) {
var data = result.d
$("#textbox").text(data);
},
If you want to see a text-book example the following looks like a good example of jQuery code which consumes a WCF web-service:
Consuming WCF service using jQuery
Hope this helps!

You need to wrap your
$.ajax({ ... })
inside a function call. That way on a specific action it is called.

Since you are not passing parameters, I would change this to a GET call instead and remove the data portion of the ajax call. Also the way you were returning it was in XML format, change the response format to JSON
[OperationContract]
[WebGet(UriTemplate = "/GetString", RequestFormat = WebMessageFormat.Json)]
public string GetString()
{
return "Hello there";
}
$.ajax({
type: "GET",
url: "http://localhost:11634/MobileService.svc/GetString",
contentType: "application/json",
dataType: "json",
success: function (result) {
$("#textbox").text(result);
},
error: function (textStatus) {
alert(textStatus);
}
});

Related

AJAX POST to WCF web config need another set of eyes/mind

For some reason I'm not able to get a very simple AJAX post to my web service (svc) to work.
I'm sure it's a problem with my web.config, but I tried everything I can find online.
In "MyService.svc" if is use
[OperationContract]
[WebInvoke(Method ="POST", RequestFormat = WebMessageFormat.Json, ResponseFormat =WebMessageFormat.Json, BodyStyle =WebMessageBodyStyle.Wrapped)]
public string DoWork()
{
string result = "did it";
//result = test;
return (new JavaScriptSerializer().Serialize(result)); ;
}
where DoWork only returns a value, Ajax succeeds. However, if I pass a parameter to DoWork(string test)
Ajax returns an error. Bad Request.
My ajax is:
function DoTest() {
testData = { "test": "Hello" };
var jsonData = JSON.stringify(testData);
var POSTURL = "MyService.svc/DoWork";
//alert(jsonData);
//alert(POSTURL);
$.ajax({
type: "POST",
url: POSTURL,
datatype: "json",
data: jsonData,
ContentType: "application/json; charset=utf-8",
success: function (result) {
alert("success: " + result.d);
},
error: function (xhr, status, error) {
alert("Opps: " + xhr + " " + status + " " + error);
}
})
}
});
This is in my web.config:
<behaviors>
<endpointBehaviors>
<behavior name="MyServiceAspNetAjaxBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<services>
<service name="MyService" behaviorConfiguration="MyServiceBehavior">
<endpoint address="" behaviorConfiguration="MyServiceAspNetAjaxBehavior"
binding="webHttpBinding" contract="MyService" />
</service>
</services>
</system.serviceModel>
I need another set of eyes on this otherwise simple program. Thanks
"Bad Request" is usually an error in the request format. You can use the help document in WCF to view the request format. If you want to enable the help document, you need to change the WebMessageBodyStyle to bare:
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle =WebMessageBodyStyle.Bare)]
Then enable the help document in the configuration file:
<endpointBehaviors>
<behavior name="ESEndPointBehavior">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
View the requested format in the browser, you need to send the data in its format.
I removed an extra <endpoint address="" I had in my revision. The service call is being made from AJAX now. Phew. This WCF works great when it's working, and very difficult to diagnose when it does not work. Thanks for listening and good luck to everyone.

What is the correct url for service reference?

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.

consuming wcf service application from JQuery Ajax

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.

WCF returns Bad Request on jquery ajax call

I have a WCF Service with one GET and one POST method. The GET works well.
The frontend is a simple HTML page which uses jquery ajax calls to GET and POST methods of the WCF.
The problem is that i get a Bad Request response. I have read all of the relative posts and i think that everything is in order, as far as i can tell.
Sources:
My contracts:
[OperationContract]
[WebGet(UriTemplate = "schedule?week={week}",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare)]
[Description("Return the food schedule; specify the dates to include.")]
WeeklySchedule GetSchedule(int week);
[OperationContract]
[WebInvoke(UriTemplate="writeweek", Method = "POST",
RequestFormat=WebMessageFormat.Json,
ResponseFormat=WebMessageFormat.Json,
BodyStyle=WebMessageBodyStyle.Bare)]
[Description("Write or Update a week schedule via json data with POST")]
int WriteWeek(string jsonwk);
I have a separate Data Access Layer which does the write/read in DB and works fine.
My page source regarding the ajax calls:
function ajaxGetWeek(wk)
{
$.ajax({
cache: false,
type: "GET",
processData: false,
url: 'http://appsrv01/food/svc/foodland.svc/schedule?week='+ wk,
dataType: "json",
success: function(data) {
GetWeekSucceeded(data);
},
error: function (xhr,status,error) {
alert(error);
}
});
This works fine, it returns json format and i am able to parse it in my form.
POST ajax call that returns bad request:
function SubmitFood() {
var features = new Object(); // Create empty javascript object
features["weeknumber"] = currentWeek;
$("#TheForm textarea").each(function() { // Iterate over inputs
features[$(this).attr("name")] = $(this).val(); // Add each to features object
});
var jsontxt = JSON.stringify(features);
$.ajax({
type: "POST",
url: 'http://appsrv01/food/svc/foodland.svc/writeweek',
data: jsontxt,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function(postresult) {
WriteWeekSucceeded(postresult);
},
error: function (xhr,status,error) {
alert(error);
}
});
}
The above returns something like:
{"weeknumber":24,"DishMon":"sdfadsf","DishTue":"asdfasd","DishWed":"fasdfa","DishThu":"sdfasdfas","DishFri":"dfasdf"}
i have put breakpoints in my VS solution but it dsoesnt even get to call the method.
my app.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true"/>
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service name="FoodLandService.FoodLandService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:6969/FoodLand"/>
</baseAddresses>
</host>
<endpoint address=""
binding="webHttpBinding"
contract="ContractsClass.IFoodLandService"
behaviorConfiguration="Web" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="Web">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="false" targetFramework="4.0" />
<customErrors mode="Off"/>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
and foodland.svc:
<%# ServiceHost Service="FoodLandService.FoodLandService"
Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
My Service source:
public class FoodLandService : ContractsClass.IFoodLandService
{
public WeeklySchedule GetSchedule(int week)
{
FoodClassDB fd = new FoodClassDB();
return fd.ReadWeekDB(week);
}
public int WriteWeek(string jsonwk)
{
FoodClassDB fd = new FoodClassDB();
return fd.WriteWeekDB(jsonwk);
}
}
json is encoded fine and i cant find a reason for my error.

Converting asmx web-service to WCF web-service - why does the JSON parameter need additional quotation marks?

I have a asmx web-service that returns a list of countries for a continent. When using JQuery to call the web-service I use:
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "InternationalLookup.asmx/LoadCountries",
data: '{ continentName: "' + $(this).val() + '" }',
dataType: "json",
success: function (response) {
//..code
},
error: function (response) {
//..code
}
});
This works fine with the asmx code but when using the WCF service I have to change it to:
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "InternationalLookup.svc/LoadCountries",
**data: '{ \"continentName\": "' + $(this).val() + '" }',**
dataType: "json",
success: function (response) {
//..code
},
error: function (response) {
//..code
}
});
Note the difference in the data I have to pass in, it now requires additional quotation marks around the continent name. My WCF service and it's configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="InternationalLookupBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="InternationalLookup">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="InternationalLookupBehavior"
name="USQ.Websites.RefreshLayout.Webservices.UsqInternationalLookup">
<endpoint address="" binding="wsHttpBinding" contract="IInternationalLookup">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
[ServiceContract]
public interface IInternationalLookup
{
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
string LoadCountries(string continentName);
}
Despite having quite some troubles getting it to work I would like to know why the parameter for the WCF web-service has to be wrapped in additional quotation marks.
The JSON specification states that object member names must be enclosed by double quotes - see www.json.org - so this is what WCF enforces. I don't know why the JSON parser used by ASMX services chose to be more lax in enforcing the syntax.