<%@ Application Language="C#" %>
<%@ Import Namespace="System.Text" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Xml" %>
<script runat="server">
//The code samples are provided AS IS without warranty of any kind.
// Microsoft disclaims all implied warranties including, without limitation,
// any implied warranties of merchantability or of fitness for a particular purpose.
/*
The entire risk arising out of the use or performance of the sample scripts and documentation remains with you.
In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts
be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption,
loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts
or documentation, even if Microsoft has been advised of the possibility of such damages.
*/
// Change this string to the string used in your web services namespace. Do not include "/" at the end.
const string NAMESPACE = "http://MyNameSpace.com";
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
}
void Application_End(object sender, EventArgs e)
{
// Code that runs on application shutdown
}
void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;
// Soap Action comes in the Http Header SoapAtion
// Ex.: SoapAction: "http://MyNameSpace.com/HelloWorld"
string tempAction = ctx.Request.Headers["SoapAction"];
// Soap action is normally surrounded by quotes so we are removing for this operation temporary string. We are not changing SoapAction yet
string soapAction = String .IsNullOrEmpty(tempAction) ? null : tempAction.Replace("\"", "");
// If there is no Soap Action in the call and the namespace is coming as expected, do not bother to filter anything
if (!String .IsNullOrEmpty(soapAction) && !soapAction.StartsWith(NAMESPACE))
{
// In the end "ns" will contain the namespace as coming from the caller
// "operation" will contain only the operation without the namespace
// For example: SoapAction: "http://roguenamespace.local/HelloWorld"
// will be split so that: ns=http://roguenamespace.local and operation=HelloWord
// If the namespace is not matching the expected namespace and the operation is not matching the right operation
// the webservices will not work.
// For the scenario above, the expected Class and Method definition are:
//
// [WebService(Namespace = "http://roguenamespace.local")]
// [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// public class NoNameSpace : System.Web.Services.WebService
// (...)
// [WebMethod]
// public string HelloWorld(string Name)
//
string ns=NAMESPACE;
string operation=soapAction;
int i=soapAction.LastIndexOf('/');
if(i>=0 && i< soapAction.Length)
{
try
{
operation = soapAction.Substring(i+1);
ns = soapAction.Substring(0,i);
//
// We verified that the incoming namespace is not matching the expected namespace before,
// now we are going to adapt the SoapAction header to include the expected namespace and the intended operation
//
ctx.Request.Headers["SoapAction"] = String.Format("\"{0}/{1}\"", NAMESPACE, operation);
}
catch (Exception ex)
{
ctx.Request.Headers["SoapAction"] = String.Format("{0}<br />{1}", ex.Message, ex.StackTrace);
}
}
else
// Since we are sure by the inner if that SoapAction is not empty or null you can assign directly.
// This code only runs if there is a SoapAction without namespace - SoapAction: "HelloWorld"
{
operation = ctx.Request.Headers["SoapAction"] = String.Format("\"{0}/{1}\"", NAMESPACE, operation);
}
//
// Add original soap action to change the response as well
// This item will be included in the Http Context items and can be tested in your code later
// if you need to make decisions based on any of them
//
ctx.Items.Add(NAMESPACE, new string[]{ operation, ns });
try
{
// Now we will create a filter that will change the incoming namespace for the operation with the intended namespace (in NAMESPACE const)
// in the message body
// For example:
// This request contains the invalid namespace http://roguenamespace.local in soap body element HelloWorld
// This filter will change the xmlns attribute to match the expected namespace to the one in NAMESPACE const
//
//<?xml version="1.0" encoding="utf-8"?>
//<soap:Envelope (...)">
// <soap:Body>
// <HelloWorld xmlns="http://roguenamespace.local"> ===> Will become ==> <HelloWorld xmlns="http://MyNameSpace.com">
// <Name>string</Name>
// </HelloWorld>
// </soap:Body>
//</soap:Envelope>
//
FilterNameSpace stream = new FilterNameSpace(ctx.Request.Filter);
stream.SetChange(soapAction, NAMESPACE, operation, false);
ctx.Request.Filter = stream;
// We are also making sure the response will return the same rogue namespace send by the caller
// See details in the method SetResponseFilter
SetResponseFilter();
}
catch
{
ctx.Request.InputStream.Position = 0; // Reset position if error happens
return;
}
if (String.IsNullOrEmpty(ns))
{
}
else
{
}
}
}
void SetResponseFilter()
{
// We will add a filter to change the correct namespace in the response to the caller rogue namespace
// The response element, by default, is the operation name + Response.
// In this sample the element is HelloWorldResponse
//
// The change will look like that (only the changed line):
//
// <HelloWorldResponse xmlns="http://MyNameSpace.com"> ===> Will become ===> <HelloWorldResponse xmlns="http://roguenamespace.local">
HttpContext ctx = HttpContext.Current;
if (ctx.Items.Contains(NAMESPACE))
{
string[] reqinfo = ctx.Items[NAMESPACE] as string[];
if (reqinfo == null || reqinfo.Length < 2) return;
string opResp = String.Format("{0}Response", reqinfo[0]);
string ns = reqinfo[1];
FilterNameSpace outfilter = new FilterNameSpace(ctx.Response.Filter);
outfilter.SetChange(NAMESPACE, ns, opResp, true);
ctx.Response.Filter = outfilter;
}
}
void Application_PostRequestHandlerExecute(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
}
class FilterNameSpace : Stream
{
protected Stream inputStream;
protected Stream sinkStream;
protected bool changed;
protected string newNameSpace;
protected string originalNS;
protected string operation;
protected StringBuilder strOutput;
protected bool canWrite;
public FilterNameSpace(Stream Sink)
{
sinkStream = Sink;
changed = false;
}
public void SetChange(string OriginalNameSpace, string NewNameSpace, string Operation, bool CanWrite)
{
originalNS = OriginalNameSpace;
newNameSpace = NewNameSpace;
operation = Operation;
inputStream = new MemoryStream();
canWrite = CanWrite;
}
public void DocumentStream()
{
if(changed) return;
XmlDocument doc = new XmlDocument();
inputStream.Position = 0;
if (canWrite)
doc.Load(inputStream);
else
doc.Load(sinkStream);
System.Xml.XmlNodeList nodes = doc.GetElementsByTagName(operation);
if (nodes.Count > 0)
{
string prefix = nodes[0].GetPrefixOfNamespace(newNameSpace);
nodes[0].Attributes["xmlns"].Value = newNameSpace;
}
changed = true;
inputStream.Position = 0;
if (canWrite)
doc.Save(sinkStream);
else
doc.Save(inputStream);
inputStream.Position = 0;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override long Length
{
get {
DocumentStream();
return inputStream.Length;
}
}
public override long Position
{
get { return inputStream.Position; }
set { throw new NotSupportedException(); }
}
public override int Read(byte[] buffer, int offset, int count)
{
DocumentStream();
int c = (int)((long)count > inputStream.Length ? inputStream.Length : (long)count);
c=inputStream.Read(buffer, offset, count);
return c;
}
public override long Seek(long offset, System.IO.SeekOrigin direction)
{
throw new NotSupportedException();
}
public override void SetLength(long length)
{
throw new NotSupportedException();
}
public override void Close()
{
inputStream.Close();
sinkStream.Close();
}
public override void Flush()
{
DocumentStream();
inputStream.Flush();
sinkStream.Flush();
}
public override void Write(byte[] buffer, int offset, int count)
{
inputStream.Write(buffer, offset, count);
}
}
</script>
very nice thanks!