Using C# I'd like to take control over the reading of the HTTP Requests from a POST. Primarily to read the stream of a multipart/form-data file upload to track the stream as it's received from the client.
Using the ProcessRequest or the Async BeginProcessRequest the body has already been parsed by ASP.net / IIS.
Is there a way to override the built-in reading via a HTTPHandler, or will I have to use another mechanism?
Many thanks
Andy
Update - Added code example as requested, albeit no different to a normal class that's implemented IHttpHandler
public class MyHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
// The body has already been received by the server
// at this point.
// I need a way to access the stream being passed
// from the Client directly, before the client starts
// to actually send the Body of the Request.
}
}
It appears that you can capture the stream via the context.BeginRequest event of a HttpModule.
For example :
public class Test : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(onBeginRequest);
}
public void onBeginRequest(object sender, EventArgs e)
{
HttpContext context = (sender as HttpApplication).Context;
if( context == nul ) { return; }
if (context.Request.RawUrl.Contains("test-handler.ext"))
{
Logger.SysLog("onBeginRequest");
TestRead(context);
}
}
// Read the stream
private static void TestRead(HttpContext context)
{
using (StreamReader reader = new StreamReader(context.Request.GetBufferlessInputStream()))
{
Logger.SysLog("Start Read");
reader.ReadToEnd();
Logger.SysLog("Read Completed");
}
}
}
Really I was trying to avoid HttpModules, as they are processed for every .net request, so really I'd stil like to know how to do it via a HTTPHandler.
You can definitely do that by implementing an IHttpHandler.
This example will get you started. There is no need to override the built-in reading.
You receive all the data in the Request, and can process it as required.
Related
I've a Blazor WebAssembly Hosted application in which I've the following component:
liveStreaming.razor
#if (_isStreaming) {
<img src="#_streamUrl">
} else {
// show loading circle
}
liveStreaming.razor.cs
using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;
using System.Timers;
public partial class LiveStreaming: ComponentBase, IDisposable
{
private bool _isStreaming;
private string _streamUrl;
private string _placeholderImgUrl;
private Timer _checkConnectionTimer;
protected override async Task OnInitializedAsync() {
_isStreaming = false;
_placeholderImgUrl = "emptyImage.jpg";
_checkConnectionTimer = new Timer();
_checkConnectionTimer.Interval = 6000;
_checkConnectionTimer.Elapsed += CheckConnection;
_checkConnectionTimer.Start();
// [...]
}
private async void CheckConnection(object sender, ElapsedEventArgs e) {
_checkConnectionTimer.Stop();
if (IsConnectionEstablished()) {
_isStreaming = true;
_streamUrl = "http://192.168.0.2/axis-cgi/mjpg/video.cgi";
StateHasChanged();
} else {
_isStreaming = false;
StateHasChanged();
}
_checkConnectionTimer.Start();
}
public void Clean() {
_checkConnectionTimer.Stop();
_streamUrl = _placeholderImgUrl;
StateHasChanged();
}
public async void Dispose() {
if (_checkConnectionTimer != null) { _checkConnectionTimer.Dispose(); }
}
}
The problem is that, without the Close() method, if I navigate to another component of the Blazor app, the stream request from the img tag is not interrupted despite the Dispose() method being called. I can see this from the bandwitdh usage. Furthermore, if I come back to the page, let's say the stream bandwitdh is 3Mb/s, another 3Mb/s is added to the currently used bandwidth. And this happens every time I leave and then come back to the page. It's like http stream request is never interrupted and a new one is created every time, without removing the old one.
In order to circumvent this problem I had to add the Clean() method you see. I've had to setup a complex mechanism in order to change the currently loaded component: every time a request to navigate to a different component arrives, the mechanism calls the Clean() method on the current loaded component before invoking _navigationManager.NavigateTo("OtherComponentName"). In other words Clean() is always called just before Dispose() method.
I'm not very happy with this solution since I've had to arrange a complex mechanism in order to achieve something that should be a given. Do you know a better way to do this?
Some test I've done:
Moving the code that now lies in Clean() inside Dispose() does nothing. Even if after StateHasChanged() I invoke Task.Delay(1). I suppose once the Dispose method has been called, the component is not rendered anymore.
Changing the code in Clean() to
_checkConnectionTimer.Stop();
_isStreaming = false;
StateHasChanged();
Does nothing. It's like I have to change the img src in order to force the http stream request to stop.
Any help will be greately appreciated. Thanks.
You need to add a finalizer/deconstructor to properly call the dispose. There is a recent article that describes the proper way to create a disposable class:
EVERYTHING THAT EVERY .NET DEVELOPER NEEDS TO KNOW ABOUT DISPOSABLE TYPES: PROPERLY IMPLEMENTING THE IDISPOSABLE INTERFACE
Here is the full example code he provides in the post:
public class PdfStreamer : IDisposable
{
private bool _disposed;
private readonly MemoryStream _stream = new MemoryStream();
public PdfStreamer()
{}
~PdfStreamer() => Dispose();
protected virtual void Dispose(bool disposing)
{
if (this._disposed)
{
return;
}
if (disposing)
{
this._stream?.Dispose();
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
First of all, I want to share my scenario what i want to build -
Scenario:
I am building a client app using wpf. In some cases, I need to call a web service to get data from the server. In order to do this, I added a web reference using wsld url. And I created a ServiceManager class that will call service method. For security reason, I need to add some header info at soap xml request for example, UserToken, SAML Token and so on. I can this from my ServiceManager class. But I want to add another class which will be called before sending request to the server. In that class, I will do something like adding security header to soap xml request with request and then send it to the server.
I used SOAP Extension to fulfill my purpose and it works well. But the problem is, every-time I need to add annotation in Reference.cs (for each web service reference) file at top of the service method. I believe that there is some other easiest way to make this working better than SOAP Extension. Is there any way where I can only call the service and a delegate class will be called automatically and I don't need to add any annotation to the reference file? I will share my sample code here.
ServiceManage class:
public class ServiceManager
{
public UserDataService dataService; //web service added at Web Reference
public ServiceManager()
{
dataService = new UserDataService();
getUserServiceRequest rqst = new getUserServiceRequest();
getUserServiceResponse resp = dataService.getUser(rqst);
}
}
Reference.cs
[TraceExtensionAttribute(Name = "First")]
public getUserServiceResponse getUser([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] getUserServiceRequest request) {
object[] results = this.Invoke("getUser", new object[] {
request});
return ((getUserServiceResponse)(results[0]));
}
TraceExtensionAttribute.cs
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
private string mstrName = null;
public override Type ExtensionType
{
get { return typeof(TraceExtension); }
}
public override int Priority
{
get { return 1; }
set { }
}
public string Name
{
get { return mstrName; }
set { mstrName = value; }
}
}
TraceExtension.cs
public class TraceExtension : SoapExtension
{
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attr){//..do something}
public override void Initialize(object initializer){//..do something}
public override Stream ChainStream(Stream stream){//...do something}
public override void ProcessMessage(SoapMessage message) {//..do something}
}
Finally, I found the solution. Just through out Web Reference and add Service Reference instead. Then go to the following link. It works for me.
All,
I am trying to modify the payload of incoming object via the web API. Currently I'm using a custom formatter which inherits from JsonMediaTypeFormatter and overrides the relevant methods.
Looks like this:
public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger,
CancellationToken cancellationToken)
{
object obj = await base.ReadFromStreamAsync(type, readStream, content, formatterLogger, cancellationToken);
TrySetEventNo(obj, GetEventNo());
return obj;
}
private void TrySetEventNo(object content, long eventNo)
{
if (content is EventModelBase)
{
EventModelBase eventBase = (EventModelBase)content;
eventBase.EventNo = eventNo;
}
}
I'm using this to track every object that comes through the API.
Before all of this happens, I have a MessageHandler which is creating an event number and adding it to Request.Properties.
Trying to get the event number in the formatter which was created previously in the MessageHandler is proving difficult. Access HttpContext.Current.Items["MS_HttpRequestMessage"].Properties seems to be a different request as it does not contain the event number.
I've two questions:
Am I doing this the correctly or is there a better way?
If I am taking the correct approach, how to I get the correct Request to extract the event number?
Thanks
I've found a solution, instead of attempting to do this inside a formatter I'm now using an ActionFilterAttribute.
overriding OnActionExecuting(HttpActionContext actionContext) and enumerating action actionContext.ActionArguments.
The complete solution looks like so:
public class SetModelEventNumberActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
foreach (KeyValuePair<string, object> actionArgument in actionContext.ActionArguments)
{
TrySetEventNo(actionArgument.Value, GetEventNo(actionContext));
}
base.OnActionExecuting(actionContext);
}
private void TrySetEventNo(object content, long eventNo)
{
if (content is EventPivotRequestMessageBase)
{
EventPivotRequestMessageBase eventBase = (EventPivotRequestMessageBase)content;
eventBase.EventNo = eventNo;
}
}
private long GetEventNo(HttpActionContext actionContext)
{
long eventNo = (long)actionContext.Request.Properties[Constant.EVENT_ID];
return eventNo;
}
}
I'm trying to log all outbound requests that go to service references, including the full request and response body. I thought I had a solution using behaviorExtensions but, after deploying, it became clear that the extension was shared between multiple requests.
Here's my current code:
public class LoggingBehaviorExtender : BehaviorExtensionElement
{
public override Type BehaviorType => typeof(LoggingRequestExtender);
protected override object CreateBehavior() { return new LoggingRequestExtender(); }
}
public class LoggingRequestExtender : IClientMessageInspector, IEndpointBehavior
{
public string Request { get; private set; }
public string Response { get; private set; }
#region IClientMessageInspector
public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
Request = request.ToString();
Response = null;
return null;
}
public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
Response = reply.ToString();
}
#endregion
#region IEndpointBehavior
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
#endregion
}
Then, when I reach the point to log, I extract the behavior...
var lre = client.Endpoint.Behaviors.OfType<LoggingRequestExtender>().FirstOrDefault();
var req = lre?.Request;
var resp = lre?.Response;
Adding debugging logging to the LoggingRequestExtender, I found it was only instantiated once for multiple requests.
Is there a way to make sure this behavior class is instantiated fresh for each thread? Or is there a better way of getting the full request / response body when making service calls?
Edit / Partial answer:
Since writing this I have discovered that the value returned by BeforeSendRequest is passed into AfterReceiveReply as the correlationState so I can connect the request and response using a guid:
public virtual object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
var guid = Guid.NewGuid();
WebServiceLog.LogCallStart(guid, channel.RemoteAddress.ToString(), request.ToString());
return guid;
}
public virtual void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
Guid guid = (Guid)correlationState;
WebServiceLog.LogCallEnd(guid, reply.ToString());
}
I see two flaws to this approach. One, which is livable, is that this requires a log insert and then update rather than a single insert.
The second is more of an issue: In the case of an exception (e.g. timeout), we never hit AfterRecieveSupply so the log doesn't know what happened. I can separately log the exception...
try
{
response = client.SomeFunction(request);
}
catch (Exception ex)
{
AppLog.Error("Some function failed", ex);
}
... but I can't see a way of accessing the guid outside of BeforeSendRequest / AfterReceiveReply so I have nothing to tie the exception log to the service request log.
There are several approaches to this.
1, The situation you have described with having to log calls separately doesn't have to be like that. If your WCF service is in a non load balanced server just add the request to a MemoryCache using the Guid as a key. When the request comes in then pull off the request and log in one go. To capture the timed out calls you could run a process on a thread that would check the MemoryCache every x minutes to pull out and log (using an adequate lock to ensure thread saftey).
If the WCF service is in a load balanced environment then all you do is the same as above but store to a no sql type data store.
2, Is the code that makes the outbound calls within your scope for change? If so, you can forgo creating a behavior extension and create a bespoke message logger instead. Using a class that implements IDisposable you can write nice code like this..
RequestMessage request = new RequestMessage();
ResponseMessage response = null;
using (_messageLogger.LogMessage(request, () => response, CallContextHelper.GetContextId(), enabled))
{
response = _outboundService.DoSomething(request);
}
This will then not need another process to capture any timed out threads which will be handled in the dispose method.
If you need more clarity then let me know, hopefully this helps you...
I have added a wsdl file in my project as a service reference. The application sends SOAP messages to a particular device which then sends the response in SOAP format.
Is there a way to look at the actual SOAP message that is wrapped in XML? Having to turn on wireshark to look at the SOAP messages gets tedious.
You can use the SVCTraceViewer to trace what are the messages that are being sent to and fro for each service call. You just have to set up the config and WCF builds the log files with the .svclog extension.
More details on this tool and its associated configuration is here. This does not require any 3rd party tool or network inspectors to be run etc... This is out of the box from Microsoft.
You are probably looking for SOAP extension,
look at this post:
Get SOAP Message before sending it to the WebService in .NET
Use Fiddler to inspect the messages. Ref: Using fiddler.
In case of WCF it has a less-known way to intercept original XML - custom MessageEncoder. It works on low level, so it captures real byte content including any malformed xml.
If you want use this approach you need to wrap a standard textMessageEncoding with custom message encoder as new binding element and apply that custom binding to endpoint in your config.
Also there is an example how I did it in my project -
wrapping textMessageEncoding, logging encoder, custom binding element and config.
I just wrapped the SOAP xml writer method and then inside the method made an event when the writing is flushed:
protected override XmlWriter GetWriterForMessage(SoapClientMessage message, int bufferSize)
{
VerboseXmlWriter xmlWriter = new VerboseXmlWriter(base.GetWriterForMessage(message, bufferSize));
xmlWriter.Finished += XmlWriter_Finished;
}
The Class for the VerboseXmlWriter goes like that (just the idea):
public sealed class VerboseXmlWriter : XmlWriter
{
private readonly XmlWriter _wrappedXmlWriter;
private readonly XmlTextWriter _buffer;
private readonly System.IO.StringWriter _stringWriter;
public event EventHandler Finished;
private void OnFinished(StringPayloadEventArgs e)
{
EventHandler handler = Finished;
handler?.Invoke(this, e);
}
public VerboseXmlWriter(XmlWriter implementation)
{
_wrappedXmlWriter = implementation;
_stringWriter = new System.IO.StringWriter();
_buffer = new XmlTextWriter(_stringWriter);
_buffer.Formatting = Formatting.Indented;
}
~VerboseXmlWriter()
{
OnFinished(new StringPayloadEventArgs(_stringWriter.ToString()));
}
public override void Flush()
{
_wrappedXmlWriter.Flush();
_buffer.Flush();
_stringWriter.Flush();
}
public string Xml
{
get
{
return _stringWriter?.ToString();
}
}
public override WriteState WriteState => _wrappedXmlWriter.WriteState;
public override void Close()
{
_wrappedXmlWriter.Close();
_buffer.Close();
}
public override string LookupPrefix(string ns)
{
return _wrappedXmlWriter.LookupPrefix(ns);
}
public override void WriteBase64(byte[] buffer, int index, int count)
{
_wrappedXmlWriter.WriteBase64(buffer, index, count);
_buffer.WriteBase64(buffer, index, count);
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
_wrappedXmlWriter.WriteSurrogateCharEntity(lowChar, highChar);
_buffer.WriteSurrogateCharEntity(lowChar, highChar);
}
and so on...
Implement the interface XmlWriter with the same structure as the example overrides. I also made an event-args class to transport the SOAP message out.
public class StringPayloadEventArgs : EventArgs
{
public string Payload { get; }
public StringPayloadEventArgs(string payload)
{
Payload = payload;
}
}
You can also use the same idea for the incomming SOAP messages.