ASP.NET Web Api返回HTML内容发布至IIS 8后序列化异常如何解决?
解决ASP.NET Web API发布到IIS8后返回HTML时的序列化错误
我之前也碰到过类似的问题,本地调试好好的,一发布到IIS就出序列化错误,咱们来一步步解决它。
先看你写的API代码:
[HttpGet] public HttpResponseMessage test() { var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StringContent("<html><body>HELLO</body></html>"); response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html"); return response; }
发布到Windows Server 2012的IIS8后出现的错误信息是:
<Error> <script/> <script id="tinyhippos-injected"/> <Message>An error has occurred.</Message> <ExceptionMessage> The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'. </ExceptionMessage> <ExceptionType>System.InvalidOperationException</ExceptionType> <StackTrace/> <InnerException> <Message>An error has occurred.</Message> <ExceptionMessage> Type 'System.Net.Http.StringContent' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types. </ExceptionMessage> <ExceptionType> System.Runtime.Serialization.InvalidDataContractException </ExceptionType> <StackTrace> at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) at System.Runtime.Serialization.XmlObjectSerializerContext.GetDataContract(RuntimeTypeHandle typeHandle, Type type) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerializeReference(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteHttpResponseMessageToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract ) at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__22.MoveNext() </StackTrace> </InnerException> </Error>
问题原因
这个错误的核心是Web API框架没有直接返回你设置的HTML内容,反而尝试序列化HttpResponseMessage对象本身。本地调试正常是因为本地请求的Accept头大概率优先接受text/html,框架直接返回了内容;但IIS环境下的请求可能默认Accept是application/xml,触发了框架的XML序列化逻辑,而StringContent类型本身不支持XML序列化,所以报错了。
解决方法
给你三种靠谱的解决方式,按需选择:
方法一:改用IHttpActionResult返回(最推荐)
ASP.NET Web API推荐用IHttpActionResult来封装响应,它会自动处理内容类型,避免序列化问题:
[HttpGet] public IHttpActionResult Test() { var htmlContent = "<html><body>HELLO</body></html>"; // 直接指定内容类型为text/html return Content(HttpStatusCode.OK, htmlContent, new MediaTypeHeaderValue("text/html")); }
方法二:用ResponseMessageResult包装HttpResponseMessage
如果你习惯用HttpResponseMessage,可以用ResponseMessage方法告诉框架直接返回这个响应,不要序列化:
[HttpGet] public IHttpActionResult Test() { var response = new HttpResponseMessage(HttpStatusCode.OK); // 这里最好加上编码,避免中文乱码 response.Content = new StringContent("<html><body>HELLO</body></html>", Encoding.UTF8, "text/html"); return ResponseMessage(response); }
方法三:全局禁用XML格式化器(适合全API不需要XML响应的场景)
如果你的API主要返回HTML或JSON,不需要XML格式,可以在WebApiConfig.cs里移除XML格式化器:
public static void Register(HttpConfiguration config) { // 移除XML格式化器,框架就不会尝试用XML序列化响应 config.Formatters.Remove(config.Formatters.XmlFormatter); // 其他路由配置 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }
优先选方法一或方法二,它们不会影响其他API的行为;方法三适合整个API都不需要XML响应的场景。
内容的提问来源于stack exchange,提问作者Passa4
相关产品推荐
相关产品推荐

