How to trace ScriptService WebService requests? - asp.net

I have a SoapExtension that is intended to log all SOAP requests and responses. It works just fine for calls from an application using the MS Soap Toolkit (OnBase Workflow). But it doesn't work for calls made by $.ajax() on an html page. Here's an example:
$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/json; charset=utf-8",
dataType: "json"
});
It's calling an ASP.NET 3.5 WebService marked with WebService and ScriptService attributes:
[WebService(Namespace = XmlSerializationService.DefaultNamespace)]
[ScriptService]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class DepartmentAssigneeService : WebService
{
private readonly DepartmentAssigneeController _controller = new DepartmentAssigneeController();
/// <summary>
/// Fetches the role items.
/// </summary>
/// <returns></returns>
[WebMethod]
[SoapLog]
public ListItem[] FetchDepartmentItems()
{
return CreateListItems(_controller.FetchDepartments());
}
}
And here are the basics for the SoapExtension and SoapExtensionAttribute:
public class LoggingSoapExtension : SoapExtension, IDisposable { /*...*/ }
[AttributeUsage(AttributeTargets.Method)]
public sealed class SoapLogAttribute : SoapExtensionAttribute { /*...*/ }
Am I missing something that would allow LoggingSoapExtension to execute on $.ajax() requests?
Update
#Chris Brandsma
It might be because you are requesting Json results instead of XML via your web service (dataType: "json"). So the ScriptService attribute is being activated, but you are not sending SOAP messages.
That answers why the SoapExtension isn't working. Any suggestions for tracing with ScriptService? The only thing that comes to mind is a ScriptService base class that provides a method to log a request. But then I'd have to call that method in every WebMethod in every ScriptService WebService (I have quite a few). I'd like to use something as clean and simple as a SoapExtension attribute, if possible.

I found a solution. By using an IHttpModule I can log requests from anything (SOAP, JSON, forms, etc). In the implementation below, I've chosen to log all .asmx and .ashx requests. This replaces LoggingSoapExtension from the question.
public class ServiceLogModule : IHttpModule
{
private HttpApplication _application;
private bool _isWebService;
private int _requestId;
private string _actionUrl;
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
_application = context;
_application.BeginRequest += ContextBeginRequest;
_application.PreRequestHandlerExecute += ContextPreRequestHandlerExecute;
_application.PreSendRequestContent += ContextPreSendRequestContent;
}
#endregion
private void ContextPreRequestHandlerExecute(object sender, EventArgs e)
{
_application.Response.Filter = new CapturedStream(_application.Response.Filter,
_application.Response.ContentEncoding);
}
private void ContextBeginRequest(object sender, EventArgs e)
{
string ext = VirtualPathUtility.GetExtension(_application.Request.FilePath).ToLower();
_isWebService = ext == ".asmx" || ext == ".ashx";
if (_isWebService)
{
ITraceLog traceLog = TraceLogFactory.Create();
_actionUrl = _application.Request.Url.PathAndQuery;
StreamReader reader = new StreamReader(_application.Request.InputStream);
string message = reader.ReadToEnd();
_application.Request.InputStream.Position = 0;
_requestId = traceLog.LogRequest(_actionUrl, message);
}
}
private void ContextPreSendRequestContent(object sender, EventArgs e)
{
if (_isWebService)
{
CapturedStream stream = _application.Response.Filter as CapturedStream;
if (stream != null)
{
ITraceLog traceLog = TraceLogFactory.Create();
traceLog.LogResponse(_actionUrl, stream.StreamContent, _requestId);
}
}
}
}
I borrowed heavily from Capturing HTML generated from ASP.NET.

It might be because you are requesting Json results instead of XML via your web service (dataType: "json"). So the ScriptService attribute is being activated, but you are not sending SOAP messages.
You could change the dataType to xml and see if that works.
http://docs.jquery.com/Ajax/jQuery.ajax#options
Also, another option for logging would be Log4Net. It could be a lot more versatile for you.

Fiddler (for IE primarily, but now for firefox) or Firebug (for firefox) are invaluable tools for watching your client-side requests and responses.

Related

asp.net MVC 3/4 equivalent to a response.filter

I am in a need to intercept all of the html that will be sent to the browser and replace some tags that are there. this will need to be done globally and for every view. what is the best way to do this in ASP.NET MVC 3 or 4 using C#? In past I have done this in ASP.net Webforms using the 'response.filter' in the Global.asax (vb)
Private Sub Global_PreRequestHandlerExecute(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRequestHandlerExecute
Response.Filter = New ReplaceTags(Response.Filter)
End Sub
this calls a class I created that inherits from the system.io.stream and that walked through the html to replace all the tags.
I have no idea as to how to do this in ASP.NET MVC 4 using C#. As you might have noticed I am a completely newbee in the MVC world.
You could still use a response filter in ASP.NET MVC:
public class ReplaceTagsFilter : MemoryStream
{
private readonly Stream _response;
public ReplaceTagsFilter(Stream response)
{
_response = response;
}
public override void Write(byte[] buffer, int offset, int count)
{
var html = Encoding.UTF8.GetString(buffer);
html = ReplaceTags(html);
buffer = Encoding.UTF8.GetBytes(html);
_response.Write(buffer, offset, buffer.Length);
}
private string ReplaceTags(string html)
{
// TODO: go ahead and implement the filtering logic
throw new NotImplementedException();
}
}
and then write a custom action filter which will register the response filter:
public class ReplaceTagsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var response = filterContext.HttpContext.Response;
response.Filter = new ReplaceTagsFilter(response.Filter);
}
}
and now all that's left is decorate the controllers/actions that you want to be applied this filter:
[ReplaceTags]
public ActionResult Index()
{
return View();
}
or register it as a global action filter in Global.asax if you want to apply to all actions.
The answer is correct but. After using it for a while I came across a case when the response is split in many parts so that html is incorrect
Part 1:
<html>.....<labe
Part 2:
l/>...</html>
Also partial renders may make unexpected cases. Their html is out of the main stream too.
So my solution is to do it in the Flush method after all streaming is done.
/// <summary>
/// Insert messages and script to display on client when a partial view is returned
/// </summary>
private class ResponseFilter : MemoryStream
{
private readonly Stream _response;
private readonly IList<object> _detachMessages;
public override void Flush()
{
// add messages and remove
// filter is called for a number of methods on one page (BeginForm, RenderPartial...)
// so that we don't need to add it more than once
var html = MessageAndScript(_detachMessages);
var buffer = Encoding.UTF8.GetBytes(html);
_detachMessages.Clear();
_response.Write(buffer, 0, buffer.Length);
base.Flush();
}
public ResponseFilter(Stream response, IList<object> detachMessages)
{
_response = response;
_detachMessages = detachMessages;
}
public override void Write(byte[] buffer, int offset, int count)
{
_response.Write(buffer, offset, buffer.Length);
}
private static string MessageAndScript(IList<object> detachMessages)
{
if (detachMessages.Count == 0)
return null;
var javascript = CustomJavaScriptSerializer.Instance.Serialize(detachMessages);
return "$(function(){var messages = " + javascript + #";
// display messages
base.ajaxHelper.displayMessages(messages);
})";
}
}

using WCF Callback and asp.net to implement publish/subscribe pattern

This is my first web application with WCF. So please guide me as a new guy.
I'm trying to use WCF Callback to implement publish/subscribe pattern. I would like to send the message from UserA to UserB or UserA to every client at the moment. I got an example from here .
In my app, I use ASP.NET as a client to connect WCF service instead and I found a problem when I subscribe to WCF service.
The WCF service does not hold any other clients object. So when I call GetAllClients(_guid), it will return only 1 client which is itself.
Here is the code in ASP.NET page (I put every control inside updatePanel)
public partial class _Default : System.Web.UI.Page, AlertServiceCallback
{
private AlertServiceClient _client;
private Guid _guid = Guid.NewGuid();
protected void Page_Load(object sender, EventArgs e)
{
InstanceContext context = new InstanceContext(this);
_client = new AlertServiceClient(context);
_client.RegisterClient(_guid);
}
protected void btnGetClients_Click(object sender, EventArgs e)
{
//Try to retrive all active clients
Client[] cs = _client.GetAllClients(_guid);
List<Client> list = new List<Client>(cs);
//Bind to dropDownList to display all active clients
ddlClients.DataSource = list;
ddlClients.DataBind();
}
#region "CallBack"
public void OnRegister(string message)
{
throw new NotImplementedException();
}
public void OnMessageSending(string message)
{
throw new NotImplementedException();
}
#endregion
}
Here is the IService and Service on WCF respectively.
[ServiceContract(Name = "AlertService",Namespace = "http://www.testWcf.com/",
CallbackContract = typeof(IAlertCallBack),SessionMode = SessionMode.Required)]
public interface IAlertService
{
[OperationContract(IsOneWay = true)]
void RegisterClient(Guid id, string name);
[OperationContract(IsOneWay = false)]
List<Client> GetAllClients(Guid id);
[OperationContract(IsOneWay = true)]
void SendMessage(Guid fromId, Guid toId, string message);
}
public interface IAlertCallBack
{
[OperationContract(IsOneWay = true)]
void OnRegister(string message);
[OperationContract(IsOneWay = true)]
void OnMessageSending(string message);
}
public class AlertService : IAlertService
{
private object locker = new object();
private Dictionary<Client, IAlertCallBack> clients = new Dictionary<Client, IAlertCallBack>();
public AlertService() { }
public void RegisterClient(Guid guid)
{
IAlertCallBack callback = OperationContext.Current.GetCallbackChannel<IAlertCallBack>();
//---prevent multiple clients adding at the same time---
lock (locker)
{
clients.Add(new Client { Id = guid, Name = name }, callback);
}
}
public List<Client> GetAllClients(Guid guid)
{
//---get all the clients in dictionary---
return (from c in clients
where c.Key.Id != guid
select c.Key).ToList();
}
...
}
Questions are:
Is it possible to implement this publish/subscribe with ASP.NET and WCF callback? (I already tried on window app and it worked fine)
If it is possible, how can I keep all clients that is connecting to WCF Service so I can use those GuId to call callback method.
Thank you.
I don't know why you don't get list of clients - you should but there are much worse problems with your code.
You can't use WCF callback in ASP.NET application. It cannot work because page lives only to serve single HTTP request - it means it most probably lives only for fraction of second and so also your registration. Even If you will be able to get list of clients you will not be able to call OnRegister or OnMessageSending because there will be no proxy listening for these calls.
Even if you force proxy to live after request processing it will still notify only code behind, not your pages rendered in client browser.
Another problem is that it can work only with net.pipe or net.tcp binding. It will not work with wsDualHttp. It is very problematic to open multiple duplex clients from the same machine when using wsDualHttp.
You are doing it completely wrong. What you need is AJAX polling from client browser to asp.net which will call simple service in your chat system.

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

How to use output caching on .ashx handler

How can I use output caching with a .ashx handler? In this case I'm doing some heavy image processing and would like the handler to be cached for a minute or so.
Also, does anyone have any recommendations on how to prevent dogpiling?
There are some good sources but you want to cache you processing server side and client-side.
Adding HTTP headers should help in the client side caching
here are some Response headers to get started on..
You can spend hours tweaking them until you get the desired performance
//Adds document content type
context.Response.ContentType = currentDocument.MimeType;
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(10));
context.Response.Cache.SetMaxAge(new TimeSpan(0,10,0));
context.Response.AddHeader("Last-Modified", currentDocument.LastUpdated.ToLongDateString());
// Send back the file content
context.Response.BinaryWrite(currentDocument.Document);
As for server side caching that is a different monster... and there are plenty of caching resources out there...
you can use like this
public class CacheHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
OutputCachedPage page = new OutputCachedPage(new OutputCacheParameters
{
Duration = 60,
Location = OutputCacheLocation.Server,
VaryByParam = "v"
});
page.ProcessRequest(HttpContext.Current);
context.Response.Write(DateTime.Now);
}
public bool IsReusable
{
get
{
return false;
}
}
private sealed class OutputCachedPage : Page
{
private OutputCacheParameters _cacheSettings;
public OutputCachedPage(OutputCacheParameters cacheSettings)
{
// Tracing requires Page IDs to be unique.
ID = Guid.NewGuid().ToString();
_cacheSettings = cacheSettings;
}
protected override void FrameworkInitialize()
{
// when you put the <%# OutputCache %> directive on a page, the generated code calls InitOutputCache() from here
base.FrameworkInitialize();
InitOutputCache(_cacheSettings);
}
}
}
Old, question but the answer didn't really mentioned the server-side handling.
As in the winning answer, I would use this for the client side:
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(10));
context.Response.Cache.SetMaxAge(TimeSpan.FromMinutes(10));
and for the server side, since you are using a ashx instead of a web page, I'm assuming that you are directly writing the output to the Context.Response.
In that case you could use something like this (in this case I want to save the response based on parameter "q", and Im using a sliding window expiration)
using System.Web.Caching;
public void ProcessRequest(HttpContext context)
{
string query = context.Request["q"];
if (context.Cache[query] != null)
{
//server side caching using asp.net caching
context.Response.Write(context.Cache[query]);
return;
}
string response = GetResponse(query);
context.Cache.Insert(query, response, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(10));
context.Response.Write(response);
}
I used the following with success and thought it worthwhile to post here .
Manually controlling the ASP.NET page output cache
From http://dotnetperls.com/cache-examples-aspnet
Setting cache options in Handler.ashx files
First, you can use HTTP handlers
in ASP.NET for a faster way to server
dynamic content than Web Form pages.
Handler.ashx is the default name for
an ASP.NET generic handler. You need
to use the HttpContext parameter and
access the Response that way.
Sample code excerpted:
<%# WebHandler Language="C#" Class="Handler" %>
C# to cache response for 1 hour
using System;
using System.Web;
public class Handler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
// Cache this handler response for 1 hour.
HttpCachePolicy c = context.Response.Cache;
c.SetCacheability(HttpCacheability.Public);
c.SetMaxAge(new TimeSpan(1, 0, 0));
}
public bool IsReusable {
get {
return false;
}
}
}
The solution with the OutputCachedPage works fine, however at a price of the performance, since you need to instantiate an object derived from the System.Web.UI.Page base class.
A simple solution would be to use the Response.Cache.SetCacheability, as suggested by some of the above answers. However for the response to be cached at the server (inside Output Cache) one needs to use HttpCacheability.Server, and set a VaryByParams or VaryByHeaders (note that when using VaryByHeaders URL can't contain a query string, since the cache will be skipped).
Here's a simple example (based on https://support.microsoft.com/en-us/kb/323290):
<%# WebHandler Language="C#" Class="cacheTest" %>
using System;
using System.Web;
using System.Web.UI;
public class cacheTest : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
TimeSpan freshness = new TimeSpan(0, 0, 0, 10);
DateTime now = DateTime.Now;
HttpCachePolicy cachePolicy = context.Response.Cache;
cachePolicy.SetCacheability(HttpCacheability.Public);
cachePolicy.SetExpires(now.Add(freshness));
cachePolicy.SetMaxAge(freshness);
cachePolicy.SetValidUntilExpires(true);
cachePolicy.VaryByParams["id"] = true;
context.Response.ContentType = "application/json";
context.Response.BufferOutput = true;
context.Response.Write(context.Request.QueryString["id"]+"\n");
context.Response.Write(DateTime.Now.ToString("s"));
}
public bool IsReusable
{
get
{
return false;
}
}
}
Hint: you monitor the caching in the Performance Counters "ASP.NET Applications__Total__\Output Cache Total".

Globally log exceptions from ASP.NET [ScriptService] services

I'm using the [System.Web.Script.Services.ScriptService] tag to use web services callable from client side javascript. What I need is a way of globally logging any unhandled exceptions in those methods. On the client side, I get the error callback and can proceed from there, but I need a server-side catch to log the exception.
The guy at this url:
http://ayende.com/Blog/archive/2008/01/06/ASP.Net-Ajax-Error-Handling-and-WTF.aspx
suggests that this can't be done.
Is that accurate? Do I seriously have to go to every single webmethod in the entire system and try/catch the method as a whole.
You can use an HTTP module to capture the exception message, stack trace and exception type that is thrown by the web service method.
First some background...
If a web service method throws an exception the HTTP response has a status code of 500.
If custom errors are off then the web
service will return the exception
message and stack trace to the client
as JSON. For example:{"Message":"Exception
message","StackTrace":" at
WebApplication.HelloService.HelloWorld()
in C:\Projects\Stackoverflow
Examples\WebApplication\WebApplication\HelloService.asmx.cs:line
22","ExceptionType":"System.ApplicationException"}
When custom errors are on then the
web service returns a default message
to the client and removes the stack
trace and exception type:{"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}
So what we need to do is set custom errors off for the web service and plug in an HTTP module that:
Checks if the request is for a web service method
Checks if an exception was thrown - that is, a status code of 500 is being returned
If 1) and 2) are true then get the original JSON that would be sent to the client and replace it with the default JSON
The code below is an example of an HTTP module that does this:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
public class ErrorHandlerModule : IHttpModule {
public void Init(HttpApplication context) {
context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
context.EndRequest += OnEndRequest;
}
static void OnPostRequestHandlerExecute(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
// TODO: Update with the correct check for your application
if (context.Request.Path.StartsWith("/HelloService.asmx")
&& context.Response.StatusCode == 500) {
context.Response.Filter =
new ErrorHandlerFilter(context.Response.Filter);
context.EndRequest += OnEndRequest;
}
}
static void OnEndRequest(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
ErrorHandlerFilter errorHandlerFilter =
context.Response.Filter as ErrorHandlerFilter;
if (errorHandlerFilter == null) {
return;
}
string originalContent =
Encoding.UTF8.GetString(
errorHandlerFilter.OriginalBytesWritten.ToArray());
// If customErrors are Off then originalContent will contain JSON with
// the original exception message, stack trace and exception type.
// TODO: log the exception
}
public void Dispose() { }
}
This module uses the following filter to override the content sent to the client and to store the original bytes (which contain the exception message, stack trace and exception type):
public class ErrorHandlerFilter : Stream {
private readonly Stream _responseFilter;
public List OriginalBytesWritten { get; private set; }
private const string Content =
"{\"Message\":\"There was an error processing the request.\"" +
",\"StackTrace\":\"\",\"ExceptionType\":\"\"}";
public ErrorHandlerFilter(Stream responseFilter) {
_responseFilter = responseFilter;
OriginalBytesWritten = new List();
}
public override void Flush() {
byte[] bytes = Encoding.UTF8.GetBytes(Content);
_responseFilter.Write(bytes, 0, bytes.Length);
_responseFilter.Flush();
}
public override long Seek(long offset, SeekOrigin origin) {
return _responseFilter.Seek(offset, origin);
}
public override void SetLength(long value) {
_responseFilter.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count) {
return _responseFilter.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) {
for (int i = offset; i < offset + count; i++) {
OriginalBytesWritten.Add(buffer[i]);
}
}
public override bool CanRead {
get { return _responseFilter.CanRead; }
}
public override bool CanSeek {
get { return _responseFilter.CanSeek; }
}
public override bool CanWrite {
get { return _responseFilter.CanWrite; }
}
public override long Length {
get { return _responseFilter.Length; }
}
public override long Position {
get { return _responseFilter.Position; }
set { _responseFilter.Position = value; }
}
}
This method requires custom errors to be switched off for the web services. You would probably want to keep custom errors on for the rest of the application so the web services should be placed in a sub directory. Custom errors can be switched off in that directory only using a web.config that overrides the parent setting.
You run the Stored Procedure in the backend. Then, for a single variable, it returns more than 1 value. Because of that, a conflicts occurs, and, this error is thrown.
I know this doesn't answer the question per-say, but I went on my own quest a while back to find this out and would up empty handed. Ended up wrapping each web service call in a try/catch, and the catch calls our error logger. Sucks, but it works.
In ASP.Net it is possible to catch all run handled exceptions using a global error handler although the blog post suggest this would not work but you could experiment with this approach trying to rethrow the error in some way?
Another idea would be to look at the open source elmah (Error Logging Modules and Handlers) for ASP.Net that might help or someone in that community may have an idea.

Resources