SoapCore
SoapCore copied to clipboard
Set "default" 'Content-Type' header if none is provided
Problem
SoapCore currently throws an exception if no Content-Type
header is provided. This can happen because v1.1.0.45 of this line in
v1.1.0.45/src/SoapCore/MessageEncoder/SoapMessageEncoderDefaults.cs within the ContentTypeToEncoding
method:
var charSet = MediaTypeHeaderValue.Parse(contentType).CharSet;
Previous Behavior
In v1.0.0 this did not happen because the encodings were acquired using a different method that did not use the System.Net.Http.Headers
namespace and the MediaTypeHeaderValue
parser
v1.0.0/src/SoapCore/MessageEncoder/SoapMessageEncoderDefaults.cs
Easy Way Out
It's easy to say "make sure incoming requests have a content type, but for legacy systems this isn't always possible. If you run an external SOAP service, you may get requests that don't send this header. In order to preserve the existing behavior that 1.0.0 had (yes, some systems haven't upgraded!)
Proposed Solution
Thus, I propose
- Adding a default
Content-Type
header to ensure a value can be parsed - Using
MediaTypeHeaderValue.TryParse(...)
instead of the regular Parse and add logic accordingly - Otherwise, have a try/catch block? I dunno, but throwing an error when
null
is provided isn't ideal behavior
Yes, that should be a TryParse. The configured WriteEncoding will be used as a fallback if this method returns null, so there is already logic for that.
With that said it can be necessary to add a custom middleware to set Content-Type anyways. In my legacy service a few clients sent SOAP1.2 envelopes without Content-Type set and that fails.
I solved that with a regular asp.net middleware (affects performance, but in my case it's much more important to stay backwards compatible than to maximize performance.
const string Soap12NS = "http://www.w3.org/2003/05/soap-envelope";
app.Use(async (context, next) =>
{
if (context.Request.Method == "POST")
{
//This line allows us to set the reader for the request back at the beginning of its stream.
context.Request.EnableBuffering();
//We convert the byte[] into a string using UTF8 encoding...
var bodyAsText = Encoding.UTF8.GetString(await context.Request.Body.ReadFullyAsync());
try
{
var xdoc = XDocument.Parse(bodyAsText);
bodyAsText = xdoc.ToString();
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(bodyAsText));
//Logging!
await SoapMessageLog.WriteLog(context, bodyAsText);
//Upgrade Content-Type if Envelope is Soap1.2
if (context.Request.Headers["Content-Type"] != "application/soap+xml")
{
var envelope = xdoc.Descendants().FirstOrDefault(x => x.Name.LocalName == "Envelope");
if (envelope != null && envelope.Name.ToString().Contains(Soap12NS))
{
context.Request.Headers["Content-Type"] = "application/soap+xml";
}
}
}
catch
{ }
//..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
context.Request.Body.Position = 0;
}
await next();
}
);
Should be fixed in https://github.com/DigDes/SoapCore/pull/1033
Ha, the use case I have is not a standard SoapCore use case, so I did the "dumb" workaround by simply using request.ContentType ?? "application/xm; charset=utf-8"
using ReadMessageAsync
within SoapMessageEncoder
Appreciate the rapid change!