Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# XML POST上的模型始终为空_C#_.net_Asp.net Web Api_Http Post - Fatal编程技术网

C# XML POST上的模型始终为空

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

我目前正在进行系统间的集成,我决定使用WebApi,但我遇到了一个问题

假设我有一个模型:

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
    
  • 默认情况下,Web API使用
    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