簡化樣品但是具有複雜類型加入
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; }
}
}
此時,我可以使用Fiddler調用..
提琴手請求細節
請求報頭:
User-Agent: Fiddler
Host: localhost:54842
Content-Length: 63
請求正文:
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
...在我的控制器放置一個斷點,當我找到所求對象空值。這是因爲以下幾個因素...
- 的WebAPI默認使用的DataContractSerializer
- 小提琴手請求未指定內容類型,或charset
- 請求體不包括XML聲明
- 請求正文不包含名稱空間定義。
如果不對Web服務控制器進行任何更改,我可以修改提琴手請求以使其工作。密切關注xml POST請求正文中的名稱空間定義。此外,請確保XML聲明包含在與請求標頭匹配的正確UTF設置中。
固定提琴手請求體與複雜數據類型
請求頭工作:
User-Agent: Fiddler
Host: localhost:54842
Content-Length: 276
Content-Type: application/xml; charset=utf-16
請求正文:
<?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>
通知在請求中namepace如何指的是相同的命名空間在我的C#控制器類(種)。因爲我們沒有改變這個項目使用DataContractSerializer以外的序列化程序,並且因爲我們沒有使用特定的命名空間修飾我們的模型(類MyRequest或MyResponse),所以它假定與WebAPI控制器本身具有相同的命名空間。這不是很清楚,而且很混亂。更好的方法是定義一個特定的命名空間。
要定義特定的命名空間,我們修改控制器模型。需要添加對System.Runtime.Serialization的引用才能完成此工作。
添加命名空間
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 = "MyCustomNamespace")]
public class MyRequest
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
[DataContract(Namespace = "MyCustomNamespace")]
public class MyResponse
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
}
模型現在更新提琴手請求使用此命名空間...
與自定義命名空間提琴手要求
<?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; }
}
}
沒有命名空間提琴手請求宣告
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
其他陷阱
當心,DataContractSerializer的是個期待XML負載中的e元素默認按字母順序排列。如果XML有效負載出現故障,您可能會發現一些元素爲空(或者,如果數據類型是整數,它將默認爲零,或者如果它是bool,則默認爲false)。例如,如果沒有指定順序,下面的XML提交...
XML體元素
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Name>MyName</Name>
<Age>99</Age>
</MyRequest>
的順序不正確的......時代的價值將默認爲零。如果幾乎相同的XML發送...
XML體元素
<?xml version="1.0" encoding="utf-16"?>
<MyRequest>
<Age>99</Age>
<Name>MyName</Name>
</MyRequest>
的正確排序,則控制器的WebAPI將正確序列化和填充年齡參數。如果您希望更改默認排序,以便可以按特定順序發送XML,請將「Order」元素添加到DataMember屬性。
指定屬性順序
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(Order = 1)]
public string Name { get; set; }
[DataMember(Order = 2)]
public int Age { get; set; }
}
[DataContract(Namespace = "")]
public class MyResponse
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
}
在本例的實施例中,XML主體必須指定名稱元素之前的年齡元件正確填充。
結論
我們看到的是一個畸形的或不完整的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;
}
方案進行評估
正如其他人所指出的,DataContractSerializer的是使用XML的WebAPI項目的默認,但也有其他XML序列化器。您可以刪除DataContractSerializer,而是使用XmlSerializer。 XmlSerializer更加容忍格式錯誤的命名空間。
另一種選擇是將請求限制爲使用JSON而不是XML。我沒有執行任何分析來確定在JSON反序列化過程中是否使用了DataContractSerializer,並且JSON交互是否需要使用DataContract屬性來裝飾模型。
你是如何從調用客戶端的'POST'方法?你確定它是一個HTTP POST嗎? – wal
提琴手。此外,WebApi默認POST調用POST方法,GET GET方法,... – Wowca
是的彼得,但你是否在下拉菜單中選擇了POST? (它默認爲GET) – wal