2010-01-06 48 views
7

我一直在用這種方式敲打我的牆頭兩天,希望有人能幫我一把。我擁有的是使用WCF編寫的RESTful Web服務;沒有什麼是真的只有兩種方法接受單個字符串參數,並且還返回一個字符串。參數和返回值都是直線XML。REST風格的WCF服務在發送「原始」XML時返回400代碼

[ServiceContract] 
public interface IService 
{ 
    [OperationContract] 
    [WebGet(UriTemplate = "/method1/{data}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)] 
    string Method1(string data); 

    [OperationContract] 
    [WebGet(UriTemplate = "/method2/{data}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)] 
    string Method2(string data); 

} 

對於參數的緣故可以說,這兩種方法的實現看起來是這樣的:

public string Method1(string data) 
{ 
    return string.Format("You entered: {0}", data); 
} 

如果我轉到HTTP://myuri.com/service.svc/method1/

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You 
    entered: foo</string> 

這個偉大的工程,但如果我改變的URL:HTTP://myuri.com/service.svc/method1/ <富>我得到一個400(壞FOO是寫給瀏覽器進行以下請求)。所以我使一些示蹤劑看到使用下面的代碼是怎麼回事:

<system.diagnostics> 
<sources> 
    <source name="System.ServiceModel" 
      switchValue="All"> 
    <listeners> 
     <add name="traceListener" 
      type="System.Diagnostics.XmlWriterTraceListener" 
      initializeData= "c:\Traces.svclog" /> 
    </listeners> 
    </source> 
</sources> 

正如你可以看到我使用的開關值「全部」捕捉每一個這樣的執行過程中發生的事件服務。我使用URL格式重新運行了幾次,以驗證示蹤劑是否正在運行。然後我去了包含XML標籤foo的URL,並按照預期收到了400錯誤,但是當我回到日誌文件時,沒有在其末尾添加其他信息。這導致我相信在調用WCF服務之前顯示400錯誤。

最後,我將方法從'GET'方法切換到'POST'方法,使用WebRequest/WebResponse編寫了一些代碼,結果相同。現在我已經閱讀了一些關於在客戶端使用XmlSerializer將數據發送到服務的文章,但這違背了此服務的目的。當我使用.NET編寫服務時,很可能PHP或傳統ASP腳本將連接到此服務,並且顯然,它們無法訪問XmlSerializer。

所以我的百萬美元的問題是這樣的:是否可以向WCF中開發的RESTful Web服務發送一個'原始'XML請求,如果是這樣,怎麼辦?

P.S. 進出服務的XML不是基於任何有形的對象,它僅僅是我創建的與此服務一起使用的結構。進入的XML通過XPath進行分析,將值放入更大的XML字符串中,並傳遞給外部API。該API的結果將被處理,然後由我的RESTful服務返回。

任何幫助將不勝感激!

回答

3

Brett,謝謝你的代碼。不幸的是,我發現它在這個線程:WCF Rest parameters involving complex types,並設法此張貼之前。

在任何情況下,我已經解決了這個問題。現在我很樂意說我有一個完整的'尤里卡'時刻,一切都剛剛結束,但事實是我剛剛開始在Google上投擲首字母縮寫詞,其中一個SERP讓我看到以下鏈接:http://blogs.msdn.com/pedram/archive/2008/04/21/how-to-consume-rest-services-with-wcf.aspx

該鏈接本身並不直接解決手頭的問題,但它讓我思考如何將我的URI模板放在一起。我曾閱讀過關於如何將RESTful服務放在一起的MSDN文章http://msdn.microsoft.com/en-us/library/dd203052.aspx。在這個例子中,作者提供了幾個不同的模板,其中一些使用了一個典型的查詢字符串參數模式,另外一些則不使用。無論出於何種原因,我選擇了無法使用典型查詢字符串參數的模板,這可以在我的原始文章中看到。所以我修改我的代碼了一下,這個想出了:

[OperationContract] 
[WebGet(UriTemplate = "/method1/?xml={data}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)] 
string Method2(string data); 

注意,URI模板是唯一改變的事情;從「/ method1/{data}」更改爲「/ method1 /?xml = {data}。然後我導航到http://myuri.com/service.svc/method1/?xml=和中提琴,一切都很好!

這也是POST的問題。即使作爲鍵/值對,XML也導致了400錯誤,使用上面顯示的完全相同的URI模板,我打開Fiddler,執行POST,結果爲200 OK。感謝大家的幫助。

+0

您提到的線程包含調用WCF服務的所有可能方法的描述,它非常棒。基本上我通過WebInvoke通過RESTful實現了簡單的XML請求。 WebGet是使用GET的REST,您也可以支持JSON響應,更像MVC 4中的Web API。 – 2012-10-10 09:23:13

+0

由於您現在直接在請求元素中發送XML,是不是會限制您可以發送的xml的大小? – evilfish 2013-06-19 05:59:32

+0

我想,但是對於給定的解決方案,我從未遇到過這種情況。我想如果我遇到了一個問題,我可能會調整配置文件中的maxQueryStringLength來解決它。 – dparsons 2013-06-19 11:57:53

1

我不相信你能夠在URL中傳遞原始XML,但你可以在代碼中完成。我已經編寫了在Compact Framework上運行的RESTful Web服務的客戶端,將對象反序列化爲原始XML並通過HttpWebRequest和HttpWebResponse將其發送到服務時沒有問題。

如果您正在進行GET,您只需在代碼中構建URL即可。如果你正在做一個POST,你可以將XML附加爲一個字節數組(下面的代碼是.Net,但是你肯定可以在PHP中做類似的事情)。

private HttpWebRequest DoInvokeRequest<T>(string uri, string method, T requestBody) 
{ 
    string destinationUrl = _baseUrl + uri; 
var invokeRequest = WebRequest.Create(destinationUrl) as HttpWebRequest; 
if (invokeRequest == null) 
    return null; 

invokeRequest.Method = method; 
invokeRequest.ContentType = "text/xml"; 

byte[] requestBodyBytes = ToByteArray(requestBody); 
invokeRequest.ContentLength = requestBodyBytes.Length; 
AddRequestHeaders(invokeRequest); 

using (Stream postStream = invokeRequest.GetRequestStream()) 
    postStream.Write(requestBodyBytes, 0, requestBodyBytes.Length); 


invokeRequest.Timeout = 60000; 
return invokeRequest; 
} 

private static byte[] ToByteArray<T>(T requestBody) 
{ 
    byte[] bytes; 
using (var s = new MemoryStream()) 
{ 
    var serializer = new XmlSerializer(typeof (T)); 
    serializer.Serialize(s, requestBody); 
    bytes = s.ToArray(); 
} 
return bytes; 
} 
3

原始代碼無法工作的主要原因之一是因爲任何xml字符串數據如果在URL路徑或查詢字符串中傳遞,都需要進行網址編碼

然而,在我看來,如果你想讓客戶端將你的數據作爲xml發送到你的服務方法,那麼應該在而不是在url中完成。 URL具有不確定的最大長度,根據不同的瀏覽器,IIS版本,並在服務器上的客戶端之間的任何坐着網絡代理上。

這意味着在所述請求中,這意味着比GET其他動詞的主體發送數據。所以讓我們使用POST。

如之前宣佈「數據」參數方法簽名,而是採取了參數出UriTemplate的,並使其WebInvoke(默認動詞是POST如你所知)。

<data><![CDATA[data xml goes in here]]></data> 

爲什麼CDATA節:

[WebInvoke(UriTemplate = "/method1", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, RequestFormat = WebMessageFormat.Xml)] 
string Method2(string data); 

然後如下您的文章身體reques應該格式化?考慮目標參數類型 - 字符串。您想要在該字符串中傳遞XML。因此,您需要確保WCF序列化程序不會將數據視爲要直接讀取的複雜數據。如果您的請求格式是JSON並且您想要將JSON字符串發送到服務,情況也是如此。

+0

我一直在等待這個答案2年。 [CDATA]提及是非常寶貴的。以前我被迫傳遞JSON響應格式的編碼xml數據來解決這個問題。現在這更優雅了!我在想什麼?! – Laguna 2012-02-08 16:02:10

+0

現在我的問題是如何投票這兩次?爲什麼這不被標記爲答案? – Laguna 2012-02-08 16:03:16

+1

@Laguna - 哈哈;那麼您唯一的問題就是如果您發佈的XML也必須包含CDATA部分!維基百科詳細介紹瞭如何處理這個http://en.wikipedia.org/wiki/CDATA – 2012-02-08 16:28:18

1

從我的理解WCF服務提供RESTful API中不允許大的有效載荷。事實上,默認情況下,WCF服務是爲了讓小消息來回發送。

從我測試的機器中,我無法獲得從WCF服務返回的XML有效內容(XML文件),該服務通過大於的REST-ful Web端點公開。2.7MB

從挖掘,並通過WCF文件的地段打獵,我來到了最後的結論是:WCF是專爲小消息時緩衝模式。當切換到流式傳輸模式時,大消息可以來回傳遞。

但是,如果您以RESTful方式通過IIS公開WCF服務,那麼您無法獲得流式響應,因爲這不受支持。或者至少我永遠無法解決它。

我希望自己有一個很好的代碼示例答案,但從我自己的實驗中可以看出,存在無法從通過IIS端點公開的WCF服務返回大型XML有效內容

我的結論是,WCF是一個真正的網絡解決方案,它正在被改裝(糟糕地)到IIS中以嘗試提供web服務。我鼓勵使用ASP .NET MVC來創建RESTful Web服務,而不使用WCF。