C# XML POST上的模型始终为空
我目前正在进行系统间的集成,我决定使用WebApi,但我遇到了一个问题 假设我有一个模型:C# XML POST上的模型始终为空,c#,.net,asp.net-web-api,http-post,C#,.net,Asp.net Web Api,Http Post,我目前正在进行系统间的集成,我决定使用WebApi,但我遇到了一个问题 假设我有一个模型: public class TestModel { public string Output { get; set; } } POST方法是: public string Post(TestModel model) { return model.Output; } 我使用标题从Fiddler创建一个请求: User-Agent: Fiddler Content-Type: "applic
public class TestModel
{
public string Output { get; set; }
}
POST方法是:
public string Post(TestModel model)
{
return model.Output;
}
我使用标题从Fiddler创建一个请求:
User-Agent: Fiddler
Content-Type: "application/xml"
Accept: "application/xml"
Host: localhost:8616
Content-Length: 57
和机构:
<TestModel><Output>Sito</Output></TestModel>
Sito
方法Post
中的model
参数总是null
,我不知道为什么。
有人有线索吗?两件事:
”
,也不需要在Fiddler中接受标题值:
User-Agent: Fiddler
Content-Type: application/xml
Accept: application/xml
DataContractSerializer
进行xml序列化。因此,您需要在xml中包含类型的命名空间:
<TestModel
xmlns="http://schemas.datacontract.org/2004/07/YourMvcApp.YourNameSpace">
<Output>Sito</Output>
</TestModel>
这样,您就不需要在XML数据中使用名称空间:
<TestModel><Output>Sito</Output></TestModel>
Sito
虽然答案已经给出,但我发现还有一些其他细节值得考虑 XMLPOST的最基本示例由VisualStudio自动作为新WebAPI项目的一部分生成,但此示例使用字符串作为输入参数 Visual Studio生成的简化示例WebAPI控制器
using System.Web.Http;
namespace webAPI_Test.Controllers
{
public class ValuesController : ApiController
{
// POST api/values
public void Post([FromBody]string value)
{
}
}
}
这不是很有帮助,因为它没有解决眼前的问题。大多数POST web服务都有相当复杂的类型作为参数,并且可能有一个复杂的类型作为响应。我将扩展上面的示例,以包括复杂的请求和复杂的响应
简化的示例,但添加了复杂类型
using System.Web.Http;
namespace webAPI_Test.Controllers
{
public class ValuesController : ApiController
{
// POST api/values
public MyResponse Post([FromBody] MyRequest value)
{
var response = new MyResponse();
response.Name = value.Name;
response.Age = value.Age;
return response;
}
}
public class MyRequest
{
public string Name { get; set; }
public int Age { get; set; }
}
public class MyResponse
{
public string Name { get; set; }
public int Age { get; set; }
}
}
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
此时,我可以使用fiddler调用
小提琴手请求详细信息
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Name>MyName</Name>
<Age>99</Age>
</MyRequest>
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
请求标头:
User-Agent: Fiddler
Host: localhost:54842
Content-Length: 63
User-Agent: Fiddler
Host: localhost:54842
Content-Length: 276
Content-Type: application/xml; charset=utf-16
请求机构:
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
请求机构:
<?xml version="1.0" encoding="utf-16"?>
<MyRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/webAPI_Test.Controllers">
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
现在更新Fiddler请求以使用此命名空间
具有自定义命名空间的Fiddler请求
<?xml version="1.0" encoding="utf-16"?>
<MyRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyCustomNamespace">
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
using System.Runtime.Serialization;
using System.Web.Http;
namespace webAPI_Test.Controllers
{
public class ValuesController : ApiController
{
// POST api/values
public MyResponse Post([FromBody] MyRequest value)
{
var response = new MyResponse();
response.Name = value.Name;
response.Age = value.Age;
return response;
}
}
[DataContract(Namespace = "")]
public class MyRequest
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
[DataContract(Namespace = "")]
public class MyResponse
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
}
未声明命名空间的Fiddler请求
using System.Web.Http;
namespace webAPI_Test.Controllers
{
public class ValuesController : ApiController
{
// POST api/values
public MyResponse Post([FromBody] MyRequest value)
{
var response = new MyResponse();
response.Name = value.Name;
response.Age = value.Age;
return response;
}
}
public class MyRequest
{
public string Name { get; set; }
public int Age { get; set; }
}
public class MyResponse
{
public string Name { get; set; }
public int Age { get; set; }
}
}
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
在本例中,xml正文必须在Age元素之前指定Name元素才能正确填充
结论
public MyRequest Deserialize(string inboundXML)
{
var ms = new MemoryStream(Encoding.Unicode.GetBytes(inboundXML));
var serializer = new DataContractSerializer(typeof(MyRequest));
var request = new MyRequest();
request = (MyRequest)serializer.ReadObject(ms);
return request;
}
我们看到的是,格式错误或不完整的POST请求主体(从DataContractSerializer的角度来看)不会引发错误,而只是导致运行时问题。如果使用DataContractSerializer,我们需要满足序列化程序(特别是在名称空间周围)。我发现使用测试工具是一种很好的方法——将XML字符串传递给使用DataContractSerializer反序列化XML的函数。当无法进行反序列化时,它会抛出错误。下面是使用DataContractSerializer测试XML字符串的代码(请记住,如果实现了此功能,则需要添加对System.Runtime.Serialization的引用)
用于评估DataContractSerializer反序列化的测试代码示例
public MyRequest Deserialize(string inboundXML)
{
var ms = new MemoryStream(Encoding.Unicode.GetBytes(inboundXML));
var serializer = new DataContractSerializer(typeof(MyRequest));
var request = new MyRequest();
request = (MyRequest)serializer.ReadObject(ms);
return request;
}
选项
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Name>MyName</Name>
<Age>99</Age>
</MyRequest>
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
正如其他人所指出的,DataContractSerializer是使用XML的WebAPI项目的默认值,但是还有其他XML序列化程序。您可以删除DataContractSerializer,而使用XmlSerializer。XmlSerializer对格式错误的名称空间内容更宽容
另一种选择是将请求限制为使用JSON而不是XML。我没有执行任何分析来确定在JSON反序列化期间是否使用DataContractSerializer,如果JSON交互需要DataContract属性来装饰模型。一旦您确保将
内容类型
标题设置为应用程序/xml
,并设置config.Formatters.XmlFormatter.UseXmlSerializer=true
在WebApiConfig.cs
的Register
方法中,XML文档顶部不需要任何版本控制或编码,这一点很重要
这最后一块让我卡住了,希望这能帮助别人,节省你的时间 我花了两天时间试图解决这个问题。最终我发现外部标记需要是类型名,而不是变量名。有效地,使用POST方法作为
public string Post([FromBody]TestModel model)
{
return model.Output;
}
我在提供尸体
<model><Output>Sito</Output></model>
Sito
而不是
<TestModel><Output>Sito</Output></TestModel>
Sito
对我来说,配置中添加了多个xmlFormatter
调试时,我发现了格式化程序列表,其中有一个重复的
config.Formatters.Add(new XmlMediaTypeFormatter());
拆下那条线,它就起作用了
要检查该行的文件
- Global.acax.cs
- WebApiConfig.cs
Post
方法?你确定这是HTTP POST吗?小提琴手。此外,WebApi默认将POST调用设置为POST方法、GET to GET方法……是的,Peter,但是您是否在fiddler的下拉列表中选择了POST?(它默认为GET)当然我这样做了:)就像我说的,调用的是POST方法。一个找出错误的提示(因为Web API在这方面完全没有帮助)就是通过request.content.ReadAsStringAsync()获取请求内容,并尝试自己反序列化XML。如果出现问题,XmlSerializer将抛出一个异常,告诉您为什么它不能反序列化,而不是像WebAPI那样返回null。在我的例子中,我就是这样发现我的XML声明声明声明了UTF-16编码,而请求本身是UTF-8编码的。如果不是我自己去做反序列化的话,我永远也不会明白这一点。太棒了!UseXmlSerializer
工作正常!我把它放在Global.asax.cs
文件中,作为全局配置的一部分,但它看起来确实必须放在App\u Start\WebApiConfig.cs
中。我不知道WebAPI默认使用DataContractSerializer-谢谢提示!!!谢谢,我花了很长时间寻找这个问题的解决方案。Json也遇到了同样的问题。我用DataContract/成员属性装饰了一个模型。验证了我可以在单元测试中反序列化。能够在签名中仅包含HttpRequestMessage的情况下发布到WebApi控制器,并在其中读取和反序列化类。mi