的ASP.NET Web API无法返回XML返回的对象有一个IList 属性(ASP.NET

2019-09-29 12:59发布

我使用的ASP.NET Web API的ApiController揭露业务逻辑的Web服务。 我测试XML和JSON,因为我们对它的需求,我一直在使用招测试。 我已经把范围缩小到这样的:有一个IList<T>由于某种原因,性质势力JSON,但改变属性List<T>允许使用JSON或XML。 不幸的是,我需要这些使用IList<T>所以我怎样才能使XML列的对象与IList<T>属性?

如果我用下面的HTML头为GET http://localhost:4946/Api/MyBizLog/GetDomainObject?id=foo

Authorization: basic ***************
Accept: application/xml
Host: localhost:4946

我回来JSON如果一个异常被抛出。 如果我改变Content-TypeContent-Type: application/xml ,我得到XML如果一个异常被抛出。 如果没有抛出异常,但是,我总是得到JSON。

我打电话的方法有类似的签名public virtual MyDomainObject GetDomainObject(String id)

我如何得到它返回的内容类型为我请教成功以及失败?

我有以下WebApiConfig:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "AlternativeApi",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { }
        );

        config.Formatters.XmlFormatter.UseXmlSerializer = true;
    }
}

更多信息

我安装的WebAPI每@Darren米勒的建议追查,我也得到了以下内容:

我把一个断点在行动的第一线。 我再从提琴手发送的获得。 输出呈现以下时,停止执行在断点处:

iisexpress.exe Information: 0 : Request, Method=GET, Url=http://localhost:4946/Api/MyBizLog/GetDomainObject?id=foo, Message='http://localhost:4946/Api/MyBizLog/GetDomainObject?id=foo'
iisexpress.exe Information: 0 : Message='MyBizLog', Operation=DefaultHttpControllerSelector.SelectController
iisexpress.exe Information: 0 : Message='WebApp.Api.MyBizLogController', Operation=DefaultHttpControllerActivator.Create
iisexpress.exe Information: 0 : Message='WebApp.Api.MyBizLogController', Operation=HttpControllerDescriptor.CreateController
iisexpress.exe Information: 0 : Message='Selected action 'GetDomainObject(String id)'', Operation=ApiControllerActionSelector.SelectAction
iisexpress.exe Information: 0 : Message='Parameter 'id' bound to the value 'foo'', Operation=ModelBinderParameterBinding.ExecuteBindingAsync
iisexpress.exe Information: 0 : Message='Model state is valid. Values: id=foo', Operation=HttpActionBinding.ExecuteBindingAsync
iisexpress.exe Information: 0 : Operation=TransactionalApiFilterAttribute.ActionExecuting

然后我让执行继续,我有以下几点:

iisexpress.exe Information: 0 : Message='Action returned 'DomainObjects.MyDomainObject'', Operation=ReflectedHttpActionDescriptor.ExecuteAsync
iisexpress.exe Information: 0 : Message='Will use same 'JsonMediaTypeFormatter' formatter', Operation=JsonMediaTypeFormatter.GetPerRequestFormatterInstance
iisexpress.exe Information: 0 : Message='Selected formatter='JsonMediaTypeFormatter', content-type='application/json; charset=utf-8'', Operation=DefaultContentNegotiator.Negotiate
iisexpress.exe Information: 0 : Operation=ApiControllerActionInvoker.InvokeActionAsync, Status=200 (OK)
iisexpress.exe Information: 0 : Operation=TransactionalApiFilterAttribute.ActionExecuted, Status=200 (OK)
iisexpress.exe Information: 0 : Operation=MyBizLogController.ExecuteAsync, Status=200 (OK)
iisexpress.exe Information: 0 : Response, Status=200 (OK), Method=GET, Url=http://localhost:4946/Api/MyBizLog/GetDomainObject?id=foo, Message='Content-type='application/json; charset=utf-8', content-length=unknown'
iisexpress.exe Information: 0 : Operation=JsonMediaTypeFormatter.WriteToStreamAsync
iisexpress.exe Information: 0 : Operation=MyBizLogController.Dispose

我有一个ActionFilterAttribute不改变结果读取基本的身份验证,并告诉谁是当前用户是商业逻辑层,而是跳跃。

然而更多信息

所以,我已经把范围缩小到的IList和列表。 如果我#define WORKS ,我得到XML。 如果我#define DOESNT_WORK ,我得到JSON。 这实际上是实际运行代码。

        public class Bar
        {
        }

        public class Foo
        {
#if WORKS
            public virtual List<Bar> Bars { get; set; }
#elif DOESNT_WORK
            public virtual IList<Bar> Bars { get; set; }
#endif
        }

        [HttpPost]
        [HttpGet]
        public Foo Test()
        {
            return new Foo();
        }

Answer 1:

那是因为你使用了错误的标题。 Content-Type是用来描述你正在传输的有效载荷。 在GET的情况下,没有有效载荷,因此没有必要为Content-Type或内容长度。 你应该设置Accept头,表示你将要返回的媒体类型的偏好。



Answer 2:

@Darrel米勒有答案:

据我了解,XmlSerializer的无法处理的接口。 要么改变你的财产为List <>,实现你的类IXmlSerializable的,或使用的DataContractSerializer。 甚至更好,不要试图通过线路返回域对象。



Answer 3:

就像@Patrick提到,@达雷尔的答案是正确的。 这里我不建议一个不同的答案,这是唯一的解决方案作为一个整体,只是在这里万一别人绊倒:

控制器:

[HttpPost]
[Route("myRoute")]
[ResponseType(typeof(MyCustomModel))]
/* Note: If your response type is of type IEnumerable, i.e. IEnumerable<MyCustomModel>, then Example in Swagger will look like this: 

<?xml version="1.0"?>
<Inline Model>
    <AttributeIdProperty>string</AttributeIdProperty>
    <PropertyForElement>string</PropertyForElement>
</Inline Model>

The real output will be correct representation, however:

<ArrayOfMyCustomModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MyCustomModel AttributeIdProperty="Value for this attribute property">
    <PropertyForElement>Value for this element property</PropertyForElement>
</MyCustomModel>
</ArrayOfContentMetadata>
*/
public virtual IHttpActionResult MyMethod([FromBody]MyCustomModel myCustomModel)
{
    if (myCustomModel== null) throw new Exception("Invalid input", HttpStatusCode.BadRequest);
    return Ok(_myBusiness.MyMethod(myCustomModel);
}

模型:

public class MyCustomModel : IXmlSerializable
{
    [JsonProperty("attributeIdProperty ")]
    [XmlAttribute("AttributeIdProperty")]
    public string AttributeIdProperty { get; set; }

    [JsonProperty("propertyForElement ")]
    [XmlElement("PropertyForElement ")]
    public string PropertyForElement { get; set; }

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "MyCustomModel")
        {
            AttributeIdProperty = reader["AttributeIdProperty"];
            PropertyForElement = reader["PropertyForElement"];
            reader.Read();
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteAttributeString("AttributeIdProperty", AttributeIdProperty);
        writer.WriteElementString("PropertyForElement", PropertyForElement);
    }
}

网页API配置:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Filters.Add(new GlobalExceptionFilter());
        //below line is what's most important for this xml serialization
        config.Formatters.XmlFormatter.UseXmlSerializer = true;
    }
}

扬鞭配置:

public class SwaggerConfig
{
    public static void Register()
    {

        var swaggerHeader = new SwaggerHeader();

        ///...

        GlobalConfiguration.Configuration
            .EnableSwagger(c =>
            {
                swaggerHeader.Apply(c);
            });

扬鞭标题:

public class SwaggerHeader : IOperationFilter
{
    public string Description { get; set; }
    public string Key { get; set; }
    public string Name { get; set; }

    public void Apply(SwaggerDocsConfig c)
    {
        c.ApiKey(Key).Name(Name).Description(Description).In("header");
        c.OperationFilter(() => this);
    }

    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (!operation.produces.Contains("application/xml")) operation.produces.Add("application/xml");
        if (!operation.produces.Contains("text/xml")) operation.produces.Add("text/xml");
    }
}


文章来源: ASP.NET Web API cannot return XML if the returned object has an IList property