Greetings!
I'm new to WCF and I thought it would be similar to ASP.NET web services, but I'm unable to call a method from client-side JavaScript. My web form looks like this:
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Path="~/test.js" />
</Scripts>
<Services>
<asp:ServiceReference Path="~/MyService.svc" />
</Services>
</asp:ScriptManager>
</div>
<button onclick="test()">Click Me</button>
</form>
My service's interface looks like this:
namespace Test
{
[ServiceContract(Namespace = "Test")]
public interface IMyService
{
[OperationContract]
void DoWork();
[OperationContract]
string SayHi();
}
}
And here's its implementation:
namespace Test
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService : IMyService
{
public void DoWork()
{
}
public string SayHi()
{
return "Hello, World!";
}
}
}
And finally the JavaScript:
function test() {
Test.MyService.SayHi(SayHiSuccess, SayHiError);
}
function SayHiSuccess(result) {
alert(result[0]);
}
function SayHiError(error) {
alert(error.toString());
}
It appears that the service's SayHi() method never executes, although I'm not sure why or how to being troubleshooting. Any suggestions?
You didn't post your web.config contents. What binding are you using? You should probably be using webHttpBinding.
Also, it may help to know your .svc file contents. Although I've never tried it, I understand that you don't have to modify web.config at all if you use WebScriptServiceHostFactory as your service host factory. That's as simple as modifying your .svc file as follows:
<%# ServiceHost Service="MyService"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
%>
If all else fails, here are some resources for you:
Calling WCF Service from Javascript
Create a Simple WCF Web...
Your code looks OK. Should be working by right. You could try adding the WebService and the WebMethod attributes as well.
For debugging a WCF web service I normally use Fiddler to track HTTP calls while running the WCF code with a debugger attached (run in Visual Studio in most cases).
Related
I am using signalR in my application.
This the client side code:
<script src="Scripts/jquery-1.6.4.js" type="text/javascript"></script>
<script src="Scripts/jquery.signalR-2.2.0.js" type="text/javascript"></script>
<script src="signalr/hubs" type="text/javascript"></script>
<script type="text/javascript">
var data = $.connection.Hubclass;
alert(data); // data is undefine
</script>
My hub class is as bellow
public class Hubclass :Hub
{
public void getdata(string msg)
{
Clients.All.send(msg);
}
}
I have startup class in Owin as bellow
[assembly: OwinStartup(typeof(Startup1))]
public class Startup1
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
This is the configuration I use in web.config:
<appSettings>
<add key="owin:appStartup" value="Startup1" />
</appSettings>
In the Global.asax class file, I also add this code:
RouteTable.Routes.MapHubs();
When I fire up the application, this is the error I get:
Error CS0619 'SignalRRouteExtensions.MapHubs(RouteCollection)' is
obsolete: 'Use IAppBuilder.MapSignalR in an Owin Startup class. See
http://go.microsoft.com/fwlink/?LinkId=320578 for more details
if i remember correctly client proxy is camel-cased (generated or dynamic) so you should use
$.connection.hubclass
or add to hub class name attribute
[HubName("Hubclass")]
and also you don't need set signalr routes by RouteTable.Routes.MapHubs owin middleware will handle routing for you...
EDIT: maybe you can check whether your generated proxy was download correctly by signlar/hubs request, check your network tab at your browser. If doesn't, there can be problem with OWIN intialization, maybe you have to set namespace or friendly name at appConfig for proper Startup class detection, check startup detection
<add key="owin:appStartup" value="StartupDemo.ProductionStartup" />
I'm trying to add SignalR to my project (ASPNET MVC 4). But I can't make it work.
In the below image you can see the error I'm receiving.
I've read a lot of stackoverflow posts but none of them is resolving my issue.
This is what I did so far:
1) Ran Install-Package Microsoft.AspNet.SignalR -Pre
2) Added RouteTable.Routes.MapHubs(); in Global.asax.cs Application_Start()
3) If I go to http://localhost:9096/Gdp.IServer.Web/signalr/hubs I can see the file content
4) Added <modules runAllManagedModulesForAllRequests="true"/> to Web.Config
5) Created folder Hubs in the root of the MVC application
6) Moved jquery and signalR scripts to /Scripts/lib folder (I'm not using jquery 1.6.4, I'm using the latest)
This is my Index.cshtml
<h2>List of Messages</h2>
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" />
<input type="hidden" id="displayname" />
<ul id="discussion">
</ul>
</div>
#section pageScripts
{
<!--Reference the SignalR library. -->
<script src="#Url.Content("~/Scripts/jquery.signalR-1.0.0-rc1.min.js")" type="text/javascript"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script type="text/javascript" src="~/signalr/hubs"></script>
<script src="#Url.Content("~/Scripts/map.js")" type="text/javascript"></script>
}
This is my IServerHub.cs file (located inside Hubs folder)
namespace Gdp.IServer.Ui.Web.Hubs
{
using Microsoft.AspNet.SignalR.Hubs;
[HubName("iServerHub")]
public class IServerHub : Hub
{
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
}
}
And this is map.js
$(function () {
// Declare a proxy to reference the hub.
var clientServerHub = $.connection.iServerHub;
// Create a function that the hub can call to broadcast messages.
clientServerHub.client.broadcastMessage = function (name, message) {
$('#discussion').append('<li><strong>' + name + '</strong>: ' + message + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Html encode display name and message.
var encodedName = $('<div />').text($('#displayname').val()).html();
var encodedMsg = $('<div />').text($('#message').val()).html();
// Call the Send method on the hub.
clientServerHub.server.send(encodedName, encodedMsg);
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
The DLL's I see references for SignalR are:
Microsoft.AspNet.SignalR.Core
Microsoft.AspNet.SignalR.Owin
Microsoft.AspNet.SignalR.SystemWeb
Any ideas how to get it work?
Should I make any change because the scripts are in /Script/lib folder?
NOTE
I'm following the instruction found here on how to set up Windsor Castle to make it work with SignalR, and again, seems that the proxy cannot be created and I'm getting the same error:
Cannot read property client of undefined
meaning that the proxy to the hub was not created
This is how I have it in the server
public class IncidentServerHub : Hub
and like this in the client
var clientServerHub = $.connection.incidentServerHub;
Again, I can see the dynamically created file here:
/GdpSoftware.Server.Web/signalr/hubs
So, why is the proxy not created?
I fixed that problem by changing my js code from:
var myHub = $.connection.SentimentsHub;
to
var myHub = $.connection.sentimentsHub;
So if you have some hub with class name TestHub you must use testHub(first letter is lowercase) name in js
For those who tried to add the generated proxy file path in the bundle.
Do not include the "~/signalr/hubs" in your BundleConfig.cs.
You can have the JQuery.SignalR in the bundle:
bundles.Add(new ScriptBundle("~/bundles/signalr").Include(
"~/Scripts/jquery.signalR-{version}.js"));
But you will need to add "/signalr/hubs" it in your view:
#section Scripts {
#Scripts.Render("~/bundles/signalr")
#Scripts.Render("/signalr/hubs")
}
I hope this helps.
I had the same error message and resolved the issue by fixing a typo I had in the [HubName] attribute on the hub class - it was not exactly matching the property in the client-side javascript.
C# hub class:
[HubName("gameHub")]
public class GameHub : Hub
{
client-side javascript:
var foo = $.connection.gameHub;
"gameHub" must be the same.
hth
For ASP.Net MVC folks:
Check your _Layout.cshtml: If you are calling the jquery bundle after the #RenderBody(), you will get this error.
Resoultion: Just move the #Scripts.Render("~/bundles/jquery") to the head section or write all signalr scripts in the scripts "section"
Your hub classes must be defined as public. For example:
class ChatHub : Hub
should actually be
public class ChatHub : Hub
Ok, I've found the issue, one thing I needed to do was:
These two references were missing:
Microsoft.AspNet.SignalR.Hosting.AspNet
Microsoft.AspNet.SignalR.Hosting.Common
Now, I included them getting nuget package: WebApiDoodle.SignalR which uses those two.
My question is why those Dlls weren't added one I installed the Nuget Package:
Microsoft.AspNet.SignalR -Pre?
Bug?
Then I had this in my global.asax.cs
using SignalR;
so
RouteTable.Routes.MapHubs();
was using that one, I needed to remove that using and use this one:
using Microsoft.AspNet.SignalR;
So the MapHubs use that one, and started to work.
Hope this helps others.
Guillermo.
You should put SignalR and jQuery scripts, in correct order :
<script src="/Scripts/jquery-1.6.4.min.js" ></script>
<script src="/Scripts/jquery.signalR-1.1.4.js"></script>
<script src="/signalr/hubs"></script>
In my case, I lost Microsoft.Owin.Host.SystemWeb.dll & Microsoft.Owin.Security.dll in my project. After adding References to them, that error was solved. it's working now.
Make sure you add
app.MapSignalR();
inside startup.cs and Configuration method,
like this:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
For Asp.net Core
Add
services.AddSignalR();
in ConfigureServices methode in your startup.cs
And add
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
});
in Configure methode in your startup.cs
Making the Hub class "public" solved it for me.
Most probably, could be the wrong name given in the class and reference in JavaScript is wrong.
var chat = $.connection.chatHub; //did not work
var chat = $.connection.myHub; // this worked
This is my class
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
I have a WCF REST service consumed in an ASP.Net site, from a page, using AJAX.
I want to be able to call methods from my service async, which means I will have callback handlers in my javascript code and when the methods finish, the output will be updated. The methods should run in different threads, because each method will take different time to complete their task
I have the code semi-working, but something strange is happening because the first time I execute the code after compiling, it works, running each call in a different threads but subsequent calls blocs the service, in such a way that each method call has to wait until the last call ends in order to execute the next one. And they are running on the same thread. I have had the same problem before when I was using Page Methods, and I solved it by disabling the session in the page but I have not figured it out how to do the same when consuming WCF REST services
Note: Methods complete time (running them async should take only 7 sec and the result should be: Execute1 - Execute3 - Execute2)
Execute1 --> 2 sec
Execute2 --> 7 sec
Execute3 --> 4 sec
Output After compiling
Output subsequent calls (this is the problem)
I will post the code...I'll try to simplify it as much as I can
Service Contract
[ServiceContract(
SessionMode = SessionMode.NotAllowed
)]
public interface IMyService
{
// I have other 3 methods like these: Execute2 and Execute3
[OperationContract]
[WebInvoke(
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/Execute1",
Method = "POST")]
string Execute1(string param);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerCall
)]
public class MyService : IMyService
{
// I have other 3 methods like these: Execute2 (7 sec) and Execute3(4 sec)
public string Execute1(string param)
{
var t = Observable.Start(() => Thread.Sleep(2000), Scheduler.NewThread);
t.First();
return string.Format("Execute1 on: {0} count: {1} at: {2} thread: {3}", param, "0", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId.ToString());
}
}
ASPX page
<%# Page EnableSessionState="False" Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="RestService._Default" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
<script type="text/javascript">
function callMethodAsync(url, data) {
$("#message").append("<br/>" + new Date());
$.ajax({
cache: false,
type: "POST",
async: true,
url: url,
data: '"de"',
contentType: "application/json",
dataType: "json",
success: function (msg) {
$("#message").append("<br/> " + msg);
},
error: function (xhr) {
alert(xhr.responseText);
}
});
}
$(function () {
$("#callMany").click(function () {
$("#message").html("");
callMethodAsync("/Execute1", "hello");
callMethodAsync("/Execute2", "crazy");
callMethodAsync("/Execute3", "world");
});
});
</script>
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<input type="button" id="callMany" value="Post Many" />
<div id="message">
</div>
</asp:Content>
Web.config (relevant)
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" />
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
Global.asax
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Ignore("{resource}.axd/{*pathInfo}");
RouteTable.Routes.Add(new ServiceRoute("",
new WebServiceHostFactory(),
typeof(MyService)));
}
Edit 1
I have tried several combinations but the result is the same, I was testing using Visual Studio but now I am testing on IIS 7, and it's the same result
I have tried combinations of the following properties:
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerCall,
ConcurrencyMode = ConcurrencyMode.Multiple
)]
Also I removed the use of Rx, now I am just simulating long-process operations like this:
Thread.Sleep(2000);
But the result is the same.... After compiling, and deploying, (the first call) the service works correctly, it is executed on different threads giving the desired result, but subsequent calls run on the same thread.... I do not get it
I just noticed something, the first time after compiling works, and the last thread used is always the thread used on subsequent calls, and this thread is blocked, it's like if the other threads weren't disposed or something or if the last thread were blocked for some reason
Edit 2
This is the full code of this project (RestWCF.zip)
http://sdrv.ms/P9wW6D
I downloaded your source code to do some testing on my own. I managed to get it to work by adding this to the web.config:
<sessionState mode="Off"></sessionState>
The problem seem to be related to the Asp.Net session handling (seems to be different from WCF session).
Without this, Fiddler shows that the WCF service response is sending an ASP.NET_SessionId cookie. You Page level EnableSessionState="False" does not affect the service.
EDIT: I did some more testing to see if I could get it to work without having to turn off session state for the whole application. I got it working now. What I did was:
Create a new folder "ServiceFolder" under the root of the application
Moved the service interface and implementation to that folder
Then in Global.asax I changed the ServiceRoute registration:
RouteTable.Routes.Add(new ServiceRoute("ServiceFolder/",
new WebServiceHostFactory(),
typeof(MyService)));
In Default.aspx I changed:
$(function () {
$("#callMany").click(function () {
$("#message").html("");
callMethodAsync('<%=this.ResolveUrl("~/ServiceFolder/Execute1") %>', "hello");
callMethodAsync('<%=this.ResolveUrl("~/ServiceFolder/Execute2") %>', "crazy");
callMethodAsync('<%=this.ResolveUrl("~/ServiceFolder/Execute3") %>', "world");
});
});
Then, in order to control the cookie handling, I created an HttpModule:
using System;
using System.Web;
namespace RestService
{
public class TestPreventCookie : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.BeginRequest +=
(new EventHandler(this.Application_BeginRequest));
application.PostAcquireRequestState +=
(new EventHandler(this.Application_PostAcquireRequestState));
}
private void Application_BeginRequest(Object source, EventArgs e)
{
//prevent session cookie from reaching the service
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
if (context.Request.Path.StartsWith("/ServiceFolder"))
{
context.Request.Cookies.Remove("ASP.NET_SessionId");
}
}
private void Application_PostAcquireRequestState(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
if (context.Request.Path.StartsWith("/ServiceFolder"))
{
var s = context.Session;
if (s != null)
s.Abandon();
}
}
}
}
And then I registered the module in web.config:
<httpModules>
<add name="TestPreventCookie" type="RestService.TestPreventCookie" />
</httpModules>
Finally, I change Default.aspx to allow session:
EnableSessionState="True"
After these changes, the parallell execution of the service call works. Obligatory disclaimer:
It works on my machine
The idea is that when the calls to the service are coming in, the HttpModule inspects the URL and, when necessary, removes the ASP.NET_SessionId cookie so it doesn't reach the service. And then in the Application_PostAcquireRequestState we immediately abandon the created session so we don't unnecessarily consume server memory for a lot of empty sessions.
With this solution, you get:
Parallell execution of service calls
You can still use Session in other parts of your application
At the cost of:
But you service must be called on a recognizable URL where session cookies are not needed
The server will create and abandon a lot of sessions
If you do not need Session or need it ReadOnly, you can change SessionStateBehavior for specific svc in Global.asax.cs. The sequential blocking will stop.
protected void Application_BeginRequest(object sender, EventArgs e) {
if (Request.Path.Contains("AjaxTestWCFService.svc")) {
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.ReadOnly);
}
}
Be warned however that SessionStateBehavior.ReadOnly will prevent writing to sessions without throwing exception. Written values will be returned as null.
I think changing your ConcurrencyMode to Multiple like below would fix your thread blocking issue:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerCall,
ConcurrencyMode = ConcurrencyMode.Multiple
)]
public class MyService : IMyService
{
}
I am only passingly familiar with Reactive, but something stood out that didn't look right.
public string Execute1(string param)
{
var t = Observable.Start(() => Thread.Sleep(2000), Scheduler.NewThread);
t.First();
return string.Format("Execute1 on: {0} count: {1} at: {2} thread: {3}", param, "0", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId.ToString());
}
In this code you are doing a Thread.Sleep in a new thread, followed by First(). The First() method blocks the current thread, so these two lines are no different to Thread.Sleep(2000). That gets Reactive out of the way. If you run with this code you get the same result, indicating that you are not getting multithreading.
The first logical cause of this is missing ConcurrencyMode=ConcurrencyMode.Multiple, which I understand you have tried. The next possibility is that you are running it on the built-in ASP.Net Development Server which, unless I am mistaken, does not support multithreading. Are you running it on an IIS instance?
I am not sure what you are trying to achieve, but t.Take(1) is non-blocking.
Update The culprit is aspNetCompatibilityEnabled="true" in web.config. If it is included you get the behavior mentioned. If it is not included (default=false), you get multi-threading. I don't yet know the logic behind this. With it set to false you lose some features as described here, including HttpContext and ASP.NET Sessions.
** Further update ** If you turn off aspNetCompatibilityEnabled, you can't using routing. I have tried a number of combinations including different bindings and transferMode settings, and they fool you by seeming to work for a while, and then going back to using the same thread. I also checked worker threads and am running out of ideas.
I have a WCF service in folder "Services":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Web.Services;
using System.ServiceModel.Activation;
using System.Web.Script.Services;
namespace Application.Services
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ScriptService]
public class ProductTypeService
{
[OperationContract]
[WebMethod]
public string LoadMainGridProductType(string test)
{
return "Success";
}
}
}
and on page I try to call this service method from javascript:
$(document).ready(function () {
var counter = "test";
Application.Services.ProductTypeService.LoadMainGridProductType(counter, ResultLoadMainGridProductType, ErrorLoadMainGridProductType);
});
on page I also make conect to this service and javascript files:
<asp:ScriptManager ID="ScriptManager" runat="server">
<Scripts>
<asp:ScriptReference Path="~/Scripts/ProductType.js" />
</Scripts>
<Services>
<asp:ServiceReference Path="~/Services/ProductTypeService.svc" />
</Services>
</asp:ScriptManager>
but code fails on
Application.Services.ProductTypeService.LoadMainGridProductType(counter, ResultLoadMainGridProductType, ErrorLoadMainGridProductType);
Error : Error of implementation of Microsoft Jscript: "Application" is not certain.
How fix this???
Firstly, check to see if the WCF service is working correctly by navigating directly to it, e.g. http://localhost:(port-number)/Services/ProductTypeService.svc
I suspect that this wont work and give you some errors.
Secondly, check to make sure that the jsdebug javascript file has been generated and is included in the scripts loaded with the application.
Another person asked a similar question earlier.
How get namespace on javascript in asp.net web?
I have this service for autocomplete extender that works for 'h' for prefixText and 3 for count and returns 'hi' and 'hello' in an array:
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public string[] GetWebUploadAutoCompleteData(string prefixText, int count)
{
try
{
DAL.DAL dal = new DAL.DAL();
string[] returnValues = dal.GetWebUploadAutoCompleteData(prefixText, count);
return returnValues;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
the service class first lines:
...
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class BLL : System.Web.Services.WebService
{
...
and this is the UI:
<asp:TextBox ID="txtTest" runat="server" Width="250px"></asp:TextBox>
<cc1:AutoCompleteExtender ID="aceTest" runat="server" CompletionSetCount="3" DelimiterCharacters=";, :"
ServicePath="http://localhost:7051/UploadServices/BLL.asmx" MinimumPrefixLength="2"
Enabled="true" ServiceMethod="GetWebUploadAutoCompleteData" TargetControlID="txtTest">
</cc1:AutoCompleteExtender>
everything is correct but it is not working, please help.
As you said that your webservice and UI are seprate project so that you can't call a service that is in a different domain than the page which hosts your client-code. This is a security feature, to prevent malicious code from redirecting your harmless javascript to something nasty on the world wide web.
Solution
To access the external web service, you can build a third web service proxy in your UI project. The third service can access the external web service from server-side, and you can access this internal web service from client.
Please let me know if you have any doubts.
EDIT
If you have created proxy service in your project.Do one more thing,add following code on the page
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="AutoComplete.asmx" />
</Services>
</asp:ScriptManager>