Quantcast
Channel: Web API
Viewing all articles
Browse latest Browse all 4850

Web API: XmlMediaTypeFormatter: ModelBinding fails when posting XML using character encoding other than default

$
0
0

I've been putting together a service using Web API, and I began testing some of the functionality recently. This particular service uses XML and ISO-8859-1 as an acceptable character encoding. I've setup my WebApiConfig to use the XmlSerializer and have added ISO-8859-1 as a supported encoding to the XmlMediaTypeFormatter.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Configure XML output
        var xml = config.Formatters.XmlFormatter;
        xml.UseXmlSerializer = true;

        // Add default output encoding
        Encoding encType = Encoding.GetEncoding(Constants.default_charset);
        foreach (MediaTypeFormatter formatter in config.Formatters)
        {
            formatter.SupportedEncodings.Insert(0, encType);
        }
        
        // Set XML as the default MediaTypeFormatter
        config.Formatters.Remove(xml);
        config.Formatters.Insert(0,xml);

        // - attribute routing
        config.MapHttpAttributeRoutes();
    }
}

I added a controller function that accepts a model in the request and returns the same model in the response.

public class SimpleController : ApiController
{
    [Route("Simple")]
    public Simple PostString(Simple request)
    {
        return request;
    }
}

public class Simple
{
    public string Text { get; set; }
}

When I send a request as Content-Type: application/xml; charset=utf-8, I get back the response:

HttpRequest

POST http://localhost/Simple HTTP/1.1 Host: localhost Accept: */* Connection: Keep-Alive Content-Type: application/xml; charset=utf-8 Content-Length: 36<Simple><Text>Sample</Text></Simple>
HttpResponse

<?xml version="1.0" encoding="utf-8"?><Simple xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Text>sample</Text></Simple>

If I send the same request as Content-Type: application/xml; charset=iso-8859-1, this is the response:

HttpRequest

POST http://localhost/Simple HTTP/1.1
Host: localhost
Accept: */*
Connection: Keep-Alive
Content-Type: application/xml; charset=iso-8859-1
Content-Length: 36

<Simple><Text>Sample</Text></Simple>
HttpResponse

<?xml version="1.0" encoding="iso-8859-1"?><Simple xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:nil="true" />

It appears as if deserialization fails when using this character encoding, returning a null object. After inspecting System.Net.Http.Formatting.XmlMediaTypeFormatter, it is failing inside XmlMediaTypeFormatter.ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger).

Stack Trace:
System.Xml.XmlException: XML encoding not supported. at System.Xml.EncodingStreamWrapper.GetSupportedEncoding(Encoding encoding) at System.Xml.EncodingStreamWrapper..ctor(Stream stream, Encoding encoding) at System.Xml.XmlUTF8TextReader.SetInput(Stream stream, Encoding encoding,
XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose) at System.Net.Http.Formatting.XmlMediaTypeFormatter.
CreateXmlReader(Stream readStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.
ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) XmlMediaTypeFormatter.ReadFromStreamAsync(
System.Type type = {Name = "Simple" FullName = "WebAPI.Models.Simple"},
System.IO.Stream readStream = .Net.Http.StreamContent.ReadOnlyStream},
System.Net.Http.HttpContent content = {System.Web.Http.WebHost.HttpControllerHandler.LazyStreamContent},
System.Net.Http.Formatting.IFormatterLogger formatterLogger =
{System.Web.Http.Validation.ModelStateFormatterLogger}
) System.Xml.EncodingStreamWrapper: enum SupportedEncoding { UTF8, UTF16LE, UTF16BE, None }

System.Xml.EncodingStreamWrapper.GetSupportedEncoding throws and XmlException(XmlEncodingNotSupported) because SupportedEncoding only has a few valid encodings.

My current workaround is to extend the XmlMediaFormatter class, overriding the ReadFromStreamAsync function. Then I replace the default XmlFormatter with mine inside WebApiConfig. It may not be the best solution, but it's functional for now.

public override Task<object> ReadFromStreamAsync(Type type,
Stream readStream,
HttpContent content,
IFormatterLogger formatterLogger) { try { var task = Task<object>.Factory.StartNew(() => { var ser = new XmlSerializer(type); return ser.Deserialize(readStream); }); return task; } catch { return base.ReadFromStreamAsync(type, readStream, content, formatterLogger); } }

Edit: I modified the function because the try/catch block wasn't capturing any exceptions.

public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, 
                                                 HttpContent content, IFormatterLogger formatterLogger)
{
	// Override base function, which was only working for utf character encodings { UTF8, UTF16LE, UTF16BE }
	// Fallback to base if this fails.
	try
	{
		return Task.FromResult(ReadFromStream(type, readStream, content, formatterLogger));
	}
	catch (Exception ex)
	{
		if (formatterLogger == null)
		{
			throw;
		}
		formatterLogger.LogError(string.Empty, ex);
		return base.ReadFromStreamAsync(type, readStream, content, formatterLogger);
	}
}

private object ReadFromStream(Type type, Stream readStream, 
                              HttpContent content, IFormatterLogger formatterLogger)
{
	var ser = new XmlSerializer(type);
	return ser.Deserialize(readStream);
}

Is this behavior a bug in Web API or am I missing a setting somewhere? Shouldn't we be able to deserialize XML data with a specified character encoding?


Viewing all articles
Browse latest Browse all 4850

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>