ASP.NET Mono: How to send SOAP instead of HTML? - asp.net

I have an ASP.NET web service based on SOAP built on top of Mono.
If I throw exceptions inside the service, the exceptions remain on the HTTP+HTML level. What I'd like to do is send always all exceptions as SOAP responses, i.e. I don't have any normal aspx pages (everything should work with SOAP).
I've tried handling all exceptions in the Global.asax.cs file within the Application_Error() method, but it seems to always send the exceptions as HTML.
What I see is the generic ASP.NET error page.
My SOAP client, when pointed to the HTML, informs me that it cannot parse HTML.
Sending SOAP from the server works nicely when no exceptions are thrown.
I've studied various web sources and learned that Application_Error shouldn't be used for SOAP exception handling from this resource:
Handling and Throwing Exceptions in XML Web Services
Do I have to implement my own HTTP Module or
ExceptionUtility Class or HTTP Handler?
I am running this on my development machine:
Version information: Mono Runtime Version: 2.10.5 (Debian 2.10.5-1); ASP.NET Version: 4.0.30319.1
I am testing this with MonoDevelop's built-in xsp HTTP server inside Ubuntu 11.10.
Here is my test code:
Global.asax.cs:
using System;
using System.Collections;
using System.ComponentModel;
using System.Web;
using System.Web.SessionState;
using System.Web.Services.Protocols;
namespace SoapTaikina
{
public class Global : System.Web.HttpApplication
{
protected virtual void Application_Start (Object sender, EventArgs e)
{
Backend.Initialize();
}
protected virtual void Application_Error (Object sender, EventArgs e)
{
// This doesn't appear to be executed when Foo() throws an exception.
// I cannot catch the Foo() constructor exception here.
throw new SoapException("This is never reached.", null);
}
// These are not used in this example.
protected virtual void Session_Start (Object sender, EventArgs e) { }
protected virtual void Application_BeginRequest (Object sender, EventArgs e) { }
protected virtual void Application_EndRequest (Object sender, EventArgs e) { }
protected virtual void Application_AuthenticateRequest (Object sender, EventArgs e) { }
protected virtual void Session_End (Object sender, EventArgs e) { }
protected virtual void Application_End (Object sender, EventArgs e) { }
}
}
Foo.asmx.cs:
using System;
using System.Web;
using System.Web.Services;
namespace SoapTaikina
{
public class Foo : System.Web.Services.WebService
{
public Foo()
{
// This may throw an Exception which will not be caught in
// Application_Error().
//
// This is the problem spot.
Backend2.InitializeMoreStuff();
}
[WebMethod]
public bool DoStuff() {
// This works fine and returns a SOAP response.
return true;
}
}
}

First, theory
Application_Error is never fired according to .NET Framework requirements. This is because the pipeline that runs pages is different from the one that runs web services. Also, notice that throwing exceptions in Application_Error is a very bad idea.
I found that testing web services from the browser (where, and probably because, accept header is not set to application/soap+xml but to text/html) makes a plaintext message appear. If the client is a SOAP proxy (ie you generated in Visual Studio/MonoDevelop from the web service's WSDL) and inherits from SoapHttpClientProtocol then it is expected that the exception is always thrown as SOAP fault.
Second, practice.
You do var f = new Foo() from Application_Start. This is wrong because the web service skeleton class is instantiated fresh new on every HTTP/SOAP request, and should never be initialized in the special Application_Start method that is run on the very first request and without the request itself bein processed yet. Also, you should avoid to do complex things (that may throw exceptions) in web service's constructor. This is just a bad design, not a non-compiling or non-working solution.
Your problem probably occurs because the ASP.NET pipeline never reached the point where the request is mapped to a web service handler rather than a page handler, firing default behaviour. Tried to find the code in mono repository, no luck :)
I suggest you to first remove any WS initialization in Application_Start, and if you said you don't really need it, throw away Application_Error

Related

SignalR and .NET Client does not work on ASP.NET WebForms page

I try to build notifications for dashboard in my WebForms application under .NET 4. I have downloaded SignalR version 1.2 (both .net client and server) and prepared a simple notification example. Unfortunatelly it does not work and I can't figure why. If I type http://myserver.com/notificationSample/signalr/hubs javascript proxy appears and it looks ok.
Take a look at the implementation below, does someone see any bug?
a) Hub implementation
[HubName("NewMessage")]
public class NewMessageNotifier : Hub
{
public void NotifyDashboards()
{
Clients.All.NewMessageCreated();
}
}
b) Notification caller (server) ~/Pages/NotificationCaller.aspx
public partial class NotificationCaller : Page
{
private HubConnection connection;
private IHubProxy proxy;
protected void Page_Load(object sender, EventArgs e)
{
connection = new HubConnection( "http://myserver.com/notificationSample" );
proxy = connection.CreateHubProxy( "NewMessage" );
connection.Start().Wait();
}
// it is handler for onclick event on Button control
protected void NotifyDashboard(object sender, EventArgs e)
{
proxy.Invoke( "NotifyDashboards" ).Wait();
}
}
c) Dashboard (client, listener) ~/Pages/Dashboard.aspx
public partial class Dashboard: BasePage
{
private HubConnection connection;
protected void Page_Load(object sender, EventArgs e)
{
connection = new HubConnection( "http://myserver.com/notificationSample" );
var proxy = connection.CreateHubProxy("NewMessage");
proxy.On("NewMessageCreated", ShowNotification);
connection.Start();
}
private void ShowNotification()
{
ShowAlert("New message added!");
}
}
you are using it in the wrong way
First
both b and c are clients, the server gets started by itself, all you need is to do is to add
RouteTable.Routes.MapHubs();
to the
Application_Start
method in global.asax
Second
if you are going to use a webpage as the client, you should do it from javascript, as what you are doing now will not work because the
connection.Start()
is async and the request will end before it does anything, and it will not wait for incoming connections because all will be disposed
Now how to do it? it will take many pages here, so here are a few links
A Simple Tutorial
The Hubs Server API
The Hubs JavaScript API
and in case you missed it, a video that explains what is SignalR, how it works and a simple app which you can find here

Call ApplicationStart Method in Global.asax Without a Request

I'm publishing my asp.net site with iis( my local machine has iis v8, server's iis v7 ).
But I want to start a function in Global.asax.cs immediately without calling a page.
*When I call a page, global.asax.cs Application_Start method launches. I want to launch it without a page call request.*
namespace ProductService.XmlFeed
{
public class Global : HttpApplication
{
protected void Application_BeginRequest(Object sender, EventArgs e)
{
FtpUploaderMain.RegisterCacheEntry(); //this method I want to start without page call
}
void Application_Start(object sender, EventArgs e)
{
SimpleLogger.WriteLog("Application Started without a page call","D:\\log.txt");
RegisterRoutes();
//FtpUploaderMain.RegisterCacheEntry();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("Products", new WebServiceHostFactory(), typeof(ProductXmlFeedService)));
}
}
}
You can use PreLoad enabled feature of IIS8 -
For IIS 7.5 use application- initialization -
http://www.iis.net/downloads/microsoft/application-initialization

What's the meaning of "UseTaskFriendlySynchronizationContext"?

There is a new app setting in asp.net 4.5
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
code like this can run in asp.net 4.0
protected void Button1_Click(object sender, EventArgs e)
{
CallAysnc();
}
public void CallAysnc()
{
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(Guid.NewGuid().ToString());
WebClient client = new WebClient();
client.DownloadStringCompleted += (object sender, DownloadStringCompletedEventArgs e) =>
{
asyncOp.PostOperationCompleted(CallCompleted, e.Result);
};
client.DownloadStringAsync(new Uri("http://www.google.com"));
}
private void CallCompleted(object args)
{
Response.Write(args.ToString());
}
But it doesn't work in asp.net 4.5,and when I remove the new appsetting,it works again!
So what's the meaning of "UseTaskFriendlySynchronizationContext" ?
Regarding UseTaskFriendlySynchronizationContext, from Microsoft Forums:
That tells ASP.NET to use an entirely new asynchronous pipeline which
follows CLR conventions for kicking off asynchronous operations,
including returning threads to the ThreadPool when necessary. ASP.NET
4.0 and below followed its own conventions which went against CLR guidelines, and if the switch is not enabled it is
very easy for asynchronous methods to run synchronously, deadlock the request, or otherwise not behave as expected.
Also, I think AsyncOperationManager is intended for desktop applications. For ASP.NET apps you should be using RegisterAsyncTask and setting <%# Page Async="true", see here for more details.
So using the new c# keywords your example would be:
protected void Button1_Click(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(CallAysnc));
}
private async Task CallAysnc()
{
var res = await new WebClient().DownloadStringTaskAsync("http://www.google.com");
Response.Write(res);
}
The aim is to support the following by release but is not currently supported in the beta:
protected async void Button1_Click(object sender, EventArgs e)
{
var res = await new WebClient().DownloadStringTaskAsync("http://www.google.com");
Response.Write(res);
}
More details, quoted from ASP.NET 4.5.1 documentation for appSettings on MSDN:
aspnet:UseTaskFriendlySynchronizationContext
Specifies how asynchronous code paths in ASP.NET 4.5 behave.
...
If this key value is set to false [default], asynchronous code paths in ASP.NET 4.5 behave as they did in ASP.NET 4.0. If this key
value is set to true, ASP.NET 4.5 uses code paths that are optimized
for Task-returning APIs. Setting this compatibility switch is
mandatory for WebSockets-enabled applications, for using Task-based
asynchrony in Web Forms pages, and for certain other asynchronous
behaviors.

Global.asax events not firing in IIS 7 Integrated Mode with .net 4 routing

I just switched over to using Server 2008/IIS 7. I am running my application in integrated mode. I am using the .Net 4.0 routing feature and using extension-less urls.
My problem is that the events in the global.asax file that are after the BeginRequest don't fire.
The events fire if I hit my page using an .aspx extension but not when it doesn't have an extension.
Does anyone know what I have to do to get the Application_AcquireRequestState event to fire for routed, extension-less urls in IIS 7?
I believe the events in Global.asax only fire for requests that are processed by the standard HttpHandler (objects derived from the Page class).
To receive events for every request, you should create and register an HttpModule.
using System;
using System.Web;
namespace Sample
{
public class SampleModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += OnAcquireRequestState;
}
void OnAcquireRequestState(object sender, EventArgs e)
{
}
public void Dispose()
{
}
}
}

IHttpModule is not being called for my WebMethod

Ok, so I have an existing application to which I have added a custom HttpModule. I'm registering two events in the Init() method (PreRequestHandlerExecute and PostRequestHandlerExecute). The HttpModule gets called for every 'normal' request. But not I have created an .aspx containing a few WebMethods that are being called for ajaxifying some UI components. The WebMethod gets called nicely, but the trouble is that my HttpModule does NOT get called at all (no events, no init, even no constructor) when accessing the WebMethod. The module gets called nicely when accessing the .aspx in question as a 'normal' request. But it refuses to be called when calling the WebMethod.
My .aspx looks like this:
public partial class SelectionListService : System.Web.UI.Page
{
[WebMethod]
[ScriptMethod]
public static RadComboBoxData GetItemsAsRadComboBoxData(RadComboBoxContext context)
{
...
}
}
My HttpModule look like this:
public class MyModule : IHttpModule, IRequiresSessionState
{
public MyModule ()
{
}
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute);
context.PostRequestHandlerExecute += new EventHandler(Application_PostRequestHandlerExecute);
}
private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
...
}
private void Application_PostRequestHandlerExecute(object sender, EventArgs e)
{
...
}
}
I have been digging into this for quite some time now, but I just can't get it to work. Any ideas?
PS1: the BeginRequest, etc in global.asax.cs do get called when accessing the WebMethod.
PS2: I'm running IIS7 on Windows7.
since PageMethods must be static, an instance of the Page class with all it's events and the ASP.NET pipeline never happens. You simply get the result of your PageMethod call, and that is all.
I have a project that had the same problem. We found that the first event in the pipeline that we could get to fire for the WebMethods was the AcquireRequestState event. We hooked into that with our HttpModule in order to do the authorization checking required for the application.
I don't know what your pre and post request handlers do, but maybe you could shift some of the logic into the AcquireRequestState event handler.

Resources