In a previous post I pointed out how to call an ASP.NET ASMX web service from javascript in the browser. Although this still works fine, the world has moved on to .NET 3.5 and WCF services have now replaced ASMX services. Luckily, these are also callable from javascript although the procedure is now a little bit different. In this sample I presume you can use Visual Studio 2008 and know a little about WCF.
1. Create an empty solution, and add a WCF service library to it
We'll call the solution JSONWCFTest, and the library JSONServices.2. Add a data contract
using System.Collections.Generic; using System.Runtime.Serialization; namespace JSONServices { [DataContract] public class SampleDataObject { public SampleDataObject(int NewId, string NewName) { Id = NewId; Name = NewName; } [DataMember] public int Id { get; set; } [DataMember] public string Name { get; set; } } }
3. Add service contract
Add an interface IJSONService with the following methods using System.Collections.Generic; using System.ServiceModel;namespace JSONServices { [ServiceContract(Namespace="http://DotNetByExample/JSONDemoService")] public interface IJSONService { [OperationContract] SampleDataObject GetSingleObject(int id, string name); [OperationContract] ICollection GetMultipleObjects(int number); } }
4. Add an implementing class
Implement the service contract like thisSystem.Collections.Generic; namespace JSONServices { public class JSONService : IJSONService { public SampleDataObject GetSingleObject(int id, string name) { return new SampleDataObject(id, name); } public ICollection<SampleDataObject> GetMultipleObjects(int number) { List<SampleDataObject> list = new List<SampleDataObject>(); for (int i = 0; i < number; i++) { list.Add(new SampleDataObject(i, "Name" + i)); } return list; } } }So far, this is very basic WCF stuff. Nothing special here.
5. Add new web application
Add a new project of type "ASP.NET Web Application" to the solution. Call it JSONTestApp. Add a reference to the JSONWCFTest project.6. Add a SVC file to the web application
This part is a bit awkward. Choose "Text File", and name it "AjaxService.svc". Then open the file, and enter the following code<%@ServiceHost language="C#" debug="true" Service="JSONServices.JSONService"It may be tempting to add an "Ajax-enabled WCF service" to the project but this creates its own operation contract, and in this example I want to simulate the situation in which I re-use an already existing operation contract implemented in a separate service library.
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"%>
7. Check the javascript interface
Basically, you are done now. Your service is hosted callable from javascript. You can check right-clicking on AjaxService.svc, and you will now see a web page that will say something like "This is a Windows© Communication Foundation service. Metadata publishing for this service is currently disabled." and a lot things more. You can check the javascript interface by adding "/jsdebug" to the url displayed in your browser, in my case this is "http://localhost:1781/AjaxService.svc/jsdebug". This shows the dynamically generated javascript proxy that you can call in your client code:Type.registerNamespace('dotnetbyexample.JSONDemoService'); dotnetbyexample.JSONDemoService.IJSONService=function() { dotnetbyexample.JSONDemoService.IJSONService.initializeBase(this); this._timeout = 0; this._userContext = null; this._succeeded = null; this._failed = null; } dotnetbyexample.JSONDemoService.IJSONService.prototype={ _get_path:function() { var p = this.get_path(); if (p) return p; else return dotnetbyexample.JSONDemoService.IJSONService._staticInstance.get_path();}, GetSingleObject:function(id,name,succeededCallback, failedCallback, userContext) { /// <param name="id" type="Number">System.Int32</param> /// <param name="name" type="String">System.String</param> /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param> /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param> /// <param name="userContext" optional="true" mayBeNull="true"></param> return this._invoke(this._get_path(), 'GetSingleObject',false,{id:id,name:name},succeededCallback,failedCallback,userContext); }, GetMultipleObjects:function(number,succeededCallback, failedCallback, userContext) { /// <param name="number" type="Number">System.Int32</param> /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param> /// <param name="failedCallback" type="Function" optional="true" TomayBeNull="true"></param> /// <param name="userContext" optional="true" mayBeNull="true"></param>Note: FireFox displays the javascript directly in the browser; for security reasons, Internet Explorer wants to download it to a file rather than display it, but after saving it and opening it in a text editor you will see the text above. As you can see this contains a lot of plumbing - and this is only the top part - but I emphasized the parts that are interesting right now: the name of the object that you need to call from javascript (dotnetbyexample.JSONDemoService.IJSONService) and the methods, which have - surprise, suprise - exactly the same name as their C# server-side counterparts. From here on, the procedure is pretty much the same as we were used to when calling asmx webservices from javascript:
8. Add an Web Form to the Web Application
Call it TestForm1.aspx9. Add a script manager and a reference
Add a script manager to the form, right below the form tag, and make a reference to the AjaxService.svc file:<form id="form1" runat="server"> <asp:scriptmanager runat="server"> <services> <asp:ServiceReference Path="AjaxService.svc" /> </services> </asp:scriptmanager>
10. Add some user interface elements to call the service
Right below the script reference, add the following code:<div> ID<asp:TextBox ID="tbId" runat="server">21</asp:TextBox> Name<asp:TextBox ID="tbName" runat="server">John Doe</asp:TextBox><br /> <asp:Button ID="btnSelect" runat="server" Text="SingleSelect" UseSubmitBehavior="False" OnClientClick="CallSingle(); return false;"/> </div> <div id="result1"></div>
11. Add Javascript code
Add some javascript code to call the WCF service and process the results. Place this inside the head-tag, below the title tag:<script type ="text/javascript"> function CallSingle() { dotnetbyexample.JSONDemoService.IJSONService.GetSingleObject( document.getElementById("tbId").value, document.getElementById("tbName").value, CallBackSingle ); } function CallBackSingle( WebServiceResult ) { resultDiv = document.getElementById("result1"); resultDiv.innerHTML = "You entered: id = " + WebServiceResult.Id + " name = " + WebServiceResult.Name; } </script>Notice again that calling a WCF method from javascript works the same as when using asmx services: one extra parameter at the end of the list is required, which is the callback. This is a function that is called upon receiving the result from the server - the server method result is put into the single parameter of the callback.
12. Run the test page
Run Test1Form.aspx. You should be able to input and numerical id and a alphanumeric name, which are sent to the server, and returned back to you as and SampleObject.13. Deploying WCF services in IIS
This all works fine when running it in Cassini, but when you want to host it in IIS, you will need to take care two things. First, you will need to ensure that IIS and WCF are correctly installed and registered. If IIS does not seem to want to cough up your SVC files (you can check that by trying http://<your host here>/<your sitename here>/AjaxService.svc), try running the command ServiceModelReg.exe /i /x from %WINDIR%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation. Restart IIS after running this command. Second, if you created a default ASP.NET 2.0 website, you may run into this error message:IIS specified authentication schemes 'IntegratedWindowsAuthentication, Anonymous', but the binding only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. Change the IIS settings so that only a single authentication scheme is used.To fix this, right-click the website in your IIS manager and choose "properties". Then click the "Directory" tab, click the "Edit" button in the "Anonymous access and authentication control" area. You will see that both Anonymous access and Integrated Windows Authentication are selected. Unselect one of them, then restart IIS. After that, the application should work smoothly.
Concluding remarks
Calling WCF services from javascript is even simpler than calling asmx services - you don't even have to mark a service as [ScriptService] anymore, you just put an .svc file into your web site with four lines of code, make a reference to an existing service library and you're done. The sample described here does not use the IJSONService.GetMultipleObjects method, but you can download a the complete source code which includes a second test page that does. Notice that although GetMultipleObjects returns a ICollection
66 comments:
Don't you hvae to define the endpoints in the webconfig of your hosting Web App?
example doesn't work if you call service from another project
Great post!
Exactly what I was looking for.
Best regards
mbs: apparently not ;-)
Anonymous: no, you cannot call the service from another project. That is not the point of an JSON service: it is supposed to communicate data from browser to server within one application. Allowing this would open the door to all kinds of cross domain attacks
Great post, but I end up with one problem. It runs like a charm within Visual Studios own webserver, but if I move it into an IIS i get a 404 on the AjaxService.svc/jsdebug file. What am I missing in the IIS settings that I get from Visual Studios webserver?
Thank you, this helped greatly.
I'm a little concerned by the previous commenter saying it doesn't work when hosted under IIS - I'm still running under the debug web server (Cassini); do you know if I'm going to need to do anything special when I publish my service to an IIS site?
@fredrik @duckboy: thanks for pointing this out, I added a section 13 with explanation about hosting the service in IIS. Have a look!
cool post thanks for that.
What about if I want to host the wcf service on the windows service not on the IIS?
You mean hosting the WCF service in a managed service and THEN calling it from javascript? If that is the case, I do not think this is possible. As to hosting a WCF service in a managed service in general, you can look http://msdn.microsoft.com/en-us/library/ms733069.aspx or http://www.codeplex.com/CmdLineService for examples. You might want to combine that with my post about running a service in VS.NET
I always get a save file dialog box if I request for the jsdebug proxy by inputting /jsdebug after the URL, say, like, http://localhost:1781/AjaxService.svc/jsdebug, why?
@Roahn: that's common Internet Explorer behaviour, I guess a security measure preventing you from actually downloading a script in the browser. FireFox will simply show the javascript in a browser window if you enter a URL like you typed.
Hi!
Thanks for the post, it explains all the stuff very well. There is however one more thing I still can't figure out: if modify service contract attribute on IJSONService to enforce WCF session (SessionMode=SessionMode.Required), then if I run AjaxService.svc in browser
Contract requires Session, but Binding 'WebHttpBinding' doesn't support it or isn't configured properly to support it. appears. It seems like the browser is using WebHttpBinding by default, while WsHttpBinding is required, right? How would you suggest to make it possible for WCF to run in SessionMode.Required in your example?
Regards,
Hi! This is a really great article. I found it very helpful.
Now what I need is to run this all on https. Unfortunately, webHttpBinding won't work.
Do you have any suggestions for running this scenario on https?
Thanks!
@Richard: I have little experience with these things in a https scenario. Cannot help you much apart from searching and that is how you probably found me ;-)
Thanks a lot! that's really a useful article for me!!
Thanx Joost! This is way better than using Json.Net :)
Thank you for the article!
I was wondering how you would put the javascript functions in the code behind...
Hi,
This is an nice article but i am getting some problem while executing this. I have problem on point number 6,7&9.
I have developed a wcf application as per your instruction,define its end point and when i browse its svc file, it is working fine.Suppose url of wcf service is http://localhost:1502/Service.svc.
Now i create new web application project to call this wcf service. Now my problem is
1- do i have to create another svc file in this web application project as per your instruction? If yes,and when i browse this svc file it gives me error of
"The type 'JSONServices.JSONService', provided as the Service attribute value in the ServiceHost directive could not be found" which is expected.
2- how to use /jsdebug. do i have to configure some thing to have this feature.
3- in point -9 path is like -"Path="AjaxService.svc". If wcf service is available on diff server then how web application come to know which server and port should application hit.
I hope answer of these problem will sort out my problems.
@Gopal let's discuss this by e-mail ;-)
Hi there, thanks for this great example. I am having a problem.
I have a simple example based on yours.
JS:
function callAjaxWFC()
{
tempuri.org.IRMService.justForJVS("John", callBack());
}
function callBack(r) {
resultDiv = document.getElementById("results");
resultDiv.innerHTML = r;
}
C#:
public string justForJVS(string i)
{
return "Hello " + i;
}
with all the plumbing as per your example.
I put breaks in the js callBack and in the C# wfc function.
what happens is the JS break is hit before the C# break - instead of the other way round.
any help please, thanks
@thewandmaker Can you please e-mail me your solution
How to call with javascript or JSON or ASP.NET AJAX if we are using Self Host WCF ?
I found nothing about that method in inet.
It's always call from same domain. :(
anyone has seen it ?
thx alot..
/jsdebug does not work.
I modified my WCF service svc file to
<%@ ServiceHost Language="C#" Debug="true" Service="MyApp.Service.StockService"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
CodeBehind="StockService.cs" %>
typing
Tryingo to test it by
http://localhost:1839/MySvc.svc/jsdebug
returns
HTTP/1.1 400 Bad Request
Server: ASP.NET Development Server/10.0.0.0
Date: Tue, 02 Nov 2010 17:19:30 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Length: 0
Connection: Close
How to get generated proxy ?
@Andus, Can you please e-mail me your solution so I can have a look at it? It should work. Really ;-)
Nice post thanks
Very good article, point to point description.
How would one configure the binding endpoints (for example to change the maxReceivedMessageSize or reader quotas)?
I followed the sample up to point 7 (viewing the service in browser) and I received the following error:
The type 'JSONServices.JSONService', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.
@octav wel... the only thing I can say check and double check you code. Or download the sample and do a side-by-side comparison
How to call a net.tcp enabled service from Ajax. My services returns data in chunks.
@Sanjay I am afraid I have no idea
Great post..! Can u please tell how to call WCF service from javascript which is hosted in IIS7.0..
Regards,
Rajesh..
@RajeshM well it works the same. The actual mouseclick are different, but you still have to disable anything but Anonymous authentication.
Great post! Everything works fine but I can't get a value / result set to call back in javascript. It comes up null. Should I be setting a callback parameter in [ServiceContract(CallbackContract)]. I can see the returned values when I'm in FF running debugger but not in IE. Otherwise it's great. I do a WCF call to add a record via LINQ and return two integers as part of a class but comes up null.. Any recommendations would be helpful..
How do you call a wcf service library hosted in a console application to be called by ajax client. I have modified your project and hosted the wcf service library in a console window and added one more web form to test this. I have added scriptmanager code as follows . but its not working it says dotnetbyexample is not defined or null when opened in browser.
@far I am not sure this is possible. The AJAX client only wants to call back to services hosted on the server it comes from itself.
@jimb hard to say what's going wrong without actually seeing your code. You did mark your return type [Serializable]? And have you tried using Fiddler?
A great post indeed!
We have implemented exactly the same approach in our web application.
This approach is working fine in the dev and test environment. When we move to the production server it starts causing issues - it becomes inaccessible.
In the production, the application is hosted on a load balanced system with 4 web servers. The application is accessed over HTTPS.
Event though the web application is accessed over HTTPS the client side request to the WCF service is still going over HTTP, which seems to be causing the issue and the service becomes inaccessible.
Please let me know, how do we ensure that the request to the internal WCF service is sent over HTTPS.
@Varin does this help? http://www.craigwardman.com/blog/index.php/2008/11/scriptmanager-service-reference-and-https/ I got this tip from fellow MVP Maurice de Beijer
Your example here is fantastic and we have managed to implement it successfully. Thank you for the work that went into the project. I have 2 things for you, one a suggestion, one a question.
1) While it is obvious after you realise your mistake, the stop of including a reference to the service project from the web project is skipped.
2) Is is possiblt to implement the svc through an htm page, or is it required to be an aspx? We are trying to build our front-end entirely html/js, so I was hoping to avoid it having to be an aspx page.
@Scott e thank you very much for pointing out my little omission! Point 5 now sports a sentence mentioning adding a reference from the web project to the library project.
is it assumed here that the wcf-svc and the page with javascript are hosted on the same server/port?
In my sample the page is hosted at(http://localhost:1197/default.aspx) and the wcf is hosted at (http://localhost/wcfservice/wcfservice.cs)
The generated javascript tries to make service call to the same url where the page is hosted (http://localhost:1197/wcfservice/wcfservice.cs/GetData). And of course it does not find this - 404.
@piyush that is correct yes, service and website are essentially hosted in once web application - i.e. on the same domain and port.
Everything works fine...but sometimes it rotates multiple times in wcf service and return the error to javascript function..
"the server method failed..status code 12152"
Hello,
First of all, thanks for your work.
Second, I was wandering what would happen if my WCF service has transport and message security enabled. With this proxy, will I be opening a hole for hackers to steal important information?
@gabo Let's be honest about it: I don't know ;-)
I had a problem calling the WCF, but this post saved me: http://stackoverflow.com/a/1016875/274589
Thanks for such a helpful tutorial. I would be glad if you can explian callback () function used in javascript
@Jui would you please care to elaborate as to what exactly it is you want to have explained?
@point 5 do we add a web service reference to JSONTestApp? a little confused at that point.
@Lenny You don't. You add a reference to the *project*. Right-click project, hit "Add reference", NOT "Add service reference".
Hello!! In Step 4 u have defined the following method:- public ICollection GetMultipleObjects(int number).
Can you please tell me how to call this method from JavaScript as it is returning a list
@Jui if you download the demo solution, which you can find here you can find a TestForm2.aspx that does exactly show to do this. The link to the demo solution is also in the article text, near the bottom, under "Concluding remarks"
This example has been a huge help for me transferring into consuming wcf service in JavaScript. I have searched high and low and cannot find a solution and I'm hoping returning to my origins will solve this :) I made a set of services for header and detail objects, and methods to retrieve and update them in the database. The header is always visible when checking the javascript interface but the detail is not. When looking closer, it shows and old method that I had deleted some weeks ago. Is there a known way to rebuild the interface? The code is nearly identical. The only difference in the classes is the name.
@Nick - this is old old code, maybe things have changed a little. Dumb question - have you tried clearing the browser cache?
Nice work
But I am unable to understand Step 7
Where should I write it and what to do with it?
http://servicePath/jsdebug is empty
what to do
@ammar I'd suggest you download the sample solution and see if that works. If it does not, it's probably something on your setup
great
Thanks Mr. Joost van Schaik I found that markup of my svc file was missing Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
@ammar great to hear that. It's quite tricky stuff indeed. You have to have all the steps right.
Nice job Joost, helped out a lot.
Great post!
Exactly what I was looking for.
Perfect explanation , Thank you
Post a Comment