Reputation: 198
I am trying to write an HttpModule that changes the request body before it arrives in an RestAPI. The goal is to add some key-values in a request body with content type "x-www-form-urlencoded". My first attempt is to only change the characters in the payload to upper case. To test this, the httpmodule is added to a simple restapi which returns the request content in the response. The logs from the httpmodule shows that the request body should be in uppercase but the RESTAPI returns the original response (which is in lower case).
The httpmodule will in the end be used in a different API which I cannot change. Here are some links that I have tried,
Here is the HttpModule code,
using IISWrapper.RequestFilters;
using NLog;
using System;
using System.Web;
using System.Web.SessionState;
namespace IISWrapper {
public class HttpModule : IRequiresSessionState, IHttpModule {
private static readonly Logger log = LogManager.GetCurrentClassLogger();
public HttpModule() { }
public void Init(HttpApplication app) {
app.BeginRequest += OnPostAuthenticateRequest;
}
public void Dispose() { }
private void OnPostAuthenticateRequest(object sender, EventArgs args) {
HttpApplication app = (HttpApplication)sender;
app.Context.Request.Filter = new RequestFilter(app.Context.Request.Filter);
}
}
}
using NLog;
using System;
using System.IO;
using System.Runtime.Remoting;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IISWrapper.RequestFilters {
class RequestFilter : Stream {
private MemoryStream ms;
private Stream _stream;
string content;
byte[] bytes;
//private Encoding _encoding;
private static readonly Logger log = LogManager.GetCurrentClassLogger();
public RequestFilter(Stream stream) {
log.Debug("RequestFilter: Initialise request filter");
_stream = stream;
//_encoding = encoding;
}
public override void Flush() {
log.Debug("Flush:");
ms.Flush();
}
public override long Seek(long offset, SeekOrigin origin) {
log.Debug("Seek:");
return ms.Seek(offset, origin);
}
public override void SetLength(long value) {
log.Debug("SetLength:");
ms.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count) {
log.Debug($"Read: Content={content}");
if (ms == null) {
log.Debug("Read: Its null:");
var sr = new StreamReader(_stream, Encoding.UTF8);
content = sr.ReadToEnd();
content = content.ToUpper();
bytes = Encoding.UTF8.GetBytes(content);
ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
ms.Seek(0, SeekOrigin.Begin);
}
log.Debug($"Read: ms.read, content={content}");
log.Debug($"Read: BytesLength={bytes.Length}");
log.Debug($"Read: BufferLength={buffer.Length}");
log.Debug($"Read: BufferToString={Encoding.UTF8.GetString(buffer)}");
log.Debug($"Read: BytesToString={Encoding.UTF8.GetString(bytes)}");
return ms.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) {
log.Debug("Write:");
throw new NotSupportedException();
}
public override bool CanRead {
get {
log.Debug("CanRead:");
return true;
}
}
public override bool CanSeek {
get {
log.Debug("CanSeek:");
return false;
}
}
public override bool CanWrite {
get {
log.Debug("CanWrite:");
return false;
}
}
public override long Length {
get {
log.Debug("Length:");
return ms.Length;
}
}
public override long Position {
get {
log.Debug("Position:");
return ms.Position;
}
set {
throw new NotSupportedException();
}
}
}
}
The HttpModule is applied to a simple REST API that only echos the request,
using log4net;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http;
namespace TestRestAPI.Controllers {
public class MyController : ApiController
{
private static readonly ILog log = LogManager.GetLogger(typeof(MyController));
[HttpPost, Route("Echo")]
public HttpResponseMessage Echo() {
try {
log.Debug("Echo: start");
byte[] byteArrayIn = Request.Content.ReadAsByteArrayAsync().Result;
string asString = Encoding.UTF8.GetString(byteArrayIn);
log.Debug($"Echo: Payload={asString}");
return Request.CreateResponse(HttpStatusCode.OK, asString);
} catch (Exception ex) {
log.Error($"Echo: Exception {ex}");
return Request.CreateResponse(HttpStatusCode.OK, ex.ToString());
}
}
}
}
The request body is "abcdefghijklmnopqrstuvwxyz1234567890" but the response from the RestApi is not in uppercase and the logs from the HttpModule is,
2021-04-28 16:34:41.3934 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: RequestFilter: Initialise request filter
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: Content=
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: Its null:
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: ms.read, content=ABCDEFGHIJKLMNOPQRSUVWXYZ1234567890
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BytesLength=35
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BufferLength=8192
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BufferToString=
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BytesToString=ABCDEFGHIJKLMNOPQRSUVWXYZ1234567890
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: Content=ABCDEFGHIJKLMNOPQRSUVWXYZ1234567890
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: ms.read, content=ABCDEFGHIJKLMNOPQRSUVWXYZ1234567890
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BytesLength=35
2021-04-28 16:34:41.6073 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BufferLength=8192
2021-04-28 16:34:41.6143 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BufferToString=ABCDEFGHIJKLMNOPQRSUVWXYZ1234567890
2021-04-28 16:34:41.6143 [7] DEBUG IISWrapper.RequestFilters.RequestFilter: Read: BytesToString=ABCDEFGHIJKLMNOPQRSUVWXYZ1234567890
Here are the logs from the rest api,
2021-04-28 16:34:41,603 [7 ] DEBUG TestRestAPI.Controllers.ApiController Echo: start
2021-04-28 16:34:41,615 [7 ] DEBUG TestRestAPI.Controllers.ApiController Echo: Payload=abcdefghijklmnopqrsuvwxyz1234567890
Any suggestion on what could be wrong is appreciated.
I found that for this to work, the endpoint needs to end with .aspx and the content type of the request must be "x-www-form-urlencoded".
Questions:
After adding the request filter i added the following line,
var _ = context.Request.Form;
to trigger the evaluation of the input stream. Then if the content type is "x-www-form-urlencoded" the request filter will be applied. If you any other kind of data like json, xml, raw text then the request content wont change for some reason. Maybe request filters only works on form data?
Upvotes: 0
Views: 1034