2013-03-28 94 views
33

我正在使用RestSharp來調用web服務。一切都很好,但我想知道是否可以打印發出的原始請求標頭和正文,以及原始響應標頭和返回的響應正文。RestSharp打印原始請求和響應標頭

這是我的代碼,我創建的請求,並得到響應

public static TResponse ExecutePostCall<TResponse, TRequest>(String url, TRequest requestData, string token= "") where TResponse : new() 
{ 
    RestRequest request = new RestRequest(url, Method.POST); 
    if (!string.IsNullOrWhiteSpace(token)) 
    { 
     request.AddHeader("TOKEN", token); 
    } 


    request.RequestFormat = DataFormat.Json; 
    request.AddBody(requestData); 

    // print raw request here 

    var response = _restClient.Execute<TResponse>(request); 

    // print raw response here 

    return response.Data; 
} 

如此,難道是可以打印原始請求和響應?

+1

你想這樣做每一次,或只是爲了調試的東西嗎?如果只是一次性使用fiddler來獲取原始請求來回 – wal

+0

不是一個完整的答案,但您可以編寫自己的序列化器/反序列化器並在那裏記錄基因處理/消耗的JSON。但是如上所述,你可能會更好地使用「嗅探」代理。 – NilsH

+0

@wal我一直在使用提琴手。我想每次都在我的.net應用程序中執行此操作。 –

回答

2

我剛剛在RestSharp的例子中找到了下面的代碼。它允許你打印你的原始迴應。

client.ExecuteAsync(request, response => 
        { 
         Console.WriteLine(response.Content); 
        }); 
-2

你可以嘗試使用

Trace.WriteLine(request.JsonSerializer.Serialize(request)); 

得到請求和

response.Content(); // as Luo have suggested 

的要求是不一樣的,作爲小提琴手錶演,但它包含的所有數據和可讀(有一些最後的RestSharp垃圾)。

+1

'Trace.WriteLine(request.JsonSerializer.Serialize(request))'不會給你** raw **請求,它所要做的就是序列化對象,這是完全不同的事情。 –

22

.net提供了自己的功能強大的日誌功能。這可以通過配置文件打開。

我發現此提示here。約翰希恩指出How to: Configure Network Tracing文章。 (注:我編輯提供的配置,關閉不必要的(對我來說)低級別的日誌記錄)。

<system.diagnostics> 
    <sources> 
     <source name="System.Net" tracemode="protocolonly" maxdatasize="1024"> 
     <listeners> 
      <add name="System.Net"/> 
     </listeners> 
     </source> 
     <source name="System.Net.Cache"> 
     <listeners> 
      <add name="System.Net"/> 
     </listeners> 
     </source> 
     <source name="System.Net.Http"> 
     <listeners> 
      <add name="System.Net"/> 
     </listeners> 
     </source> 
    </sources> 
    <switches> 
     <add name="System.Net" value="Verbose"/> 
     <add name="System.Net.Cache" value="Verbose"/> 
     <add name="System.Net.Http" value="Verbose"/> 
     <add name="System.Net.Sockets" value="Verbose"/> 
     <add name="System.Net.WebSockets" value="Verbose"/> 
    </switches> 
    <sharedListeners> 
     <add name="System.Net" 
     type="System.Diagnostics.TextWriterTraceListener" 
     initializeData="network.log" 
     /> 
    </sharedListeners> 
    <trace autoflush="true"/> 
    </system.diagnostics> 
+0

注意:請刪除偵聽器名稱中的空白區域 - 否則診斷程序會拋出「共享偵聽器未發現異常」。 – Juri

+0

輸出將顯示在具有上述配置的輸出窗口的「Debug」Feed中。確保在開始調試之前打開它。 –

+0

不幸的是,如果您使用Mono運行時,這並不會奏效。 –

2

您可以使用Fiddler來捕獲HTTP請求。

+0

有時,在嘗試排除客戶端未安裝Fiddler時遇到的問題時,能夠記錄這些類型的東西是很好的。 –

+0

我們如何在這裏使用fiddler,我們需要改變配置中的某些東西嗎? – singsuyash

+0

@singsuyash對不起,但我不記得爲什麼我在一年前回答。我再次讀到這個問題,現在我認爲這不是一個好的答案。但我看到我投了這個asnwer http://stackoverflow.com/a/21989958/1277458 – Neshta

4

您必須遍歷request.Parameters列表並將其格式化爲任意格式的字符串。

var sb = new StringBuilder(); 
foreach(var param in request.Parameters) 
{ 
    sb.AppendFormat("{0}: {1}\r\n", param.Name, param.Value); 
} 
return sb.ToString(); 

如果你想輸出顯示請求頭,然後身體類似提琴手,你只需要通過請求體訂購的請求頭集合,然後。該集合中的Parameter對象具有Type參數枚舉。

25

正如我們已經知道的RestSharp沒有提供一個機制來實現你想要的,並激活.Net跟蹤有點過分IMO。

對於日誌記錄(調試)目的(我可以在PROD中打開一段時間)我發現這種方法非常有用(儘管它有一些關於如何調用它的細節,代碼):

private void LogRequest(IRestRequest request, IRestResponse response, long durationMs) 
{ 
     var requestToLog = new 
     { 
      resource = request.Resource, 
      // Parameters are custom anonymous objects in order to have the parameter type as a nice string 
      // otherwise it will just show the enum value 
      parameters = request.Parameters.Select(parameter => new 
      { 
       name = parameter.Name, 
       value = parameter.Value, 
       type = parameter.Type.ToString() 
      }), 
      // ToString() here to have the method as a nice string otherwise it will just show the enum value 
      method = request.Method.ToString(), 
      // This will generate the actual Uri used in the request 
      uri = _restClient.BuildUri(request), 
     }; 

     var responseToLog = new 
     { 
      statusCode = response.StatusCode, 
      content = response.Content, 
      headers = response.Headers, 
      // The Uri that actually responded (could be different from the requestUri if a redirection occurred) 
      responseUri = response.ResponseUri, 
      errorMessage = response.ErrorMessage, 
     }; 

     Trace.Write(string.Format("Request completed in {0} ms, Request: {1}, Response: {2}", 
       durationMs, 
       JsonConvert.SerializeObject(requestToLog), 
       JsonConvert.SerializeObject(responseToLog))); 
} 

注意事項:

  • 接頭,地址段,查詢字符串參數,車身等出現在參數收集的所有被認爲是參數RestSharp,所有請求,與他們的對應積水型。
  • 必須在請求發生後調用日誌方法。這是因爲RestSharp的工作方式,Execute方法會添加標題,運行驗證器(如果配置了一些)等等。所有這些都會修改請求。所以爲了讓發送的所有真實參數都被記錄下來,應該在記錄請求之前調用Execute方法。
  • RestSharp本身不會拋出(相反,錯誤會保存在response.ErrorException屬性中),但我認爲反序列化可能會拋出(不確定),除此之外我需要記錄原始響應,所以我選擇實現自己的反序列化。
  • 請記住,在轉換參數值以生成Uri時,RestSharp使用自己的格式,所以序列化參數以記錄它們可能不會顯示放在Uri中的完全相同的東西。這就是爲什麼IRestClient.BuildUri方法獲得實際稱爲Uri(包括基礎URL,替換的url段,添加的queryString參數等)非常酷的原因。
  • 編輯:也要記住,它可能發生的情況是,序列化程序RestSharp正在使用的身體是不一樣的代碼正在使用,所以我猜代碼可以調整使用request.JsonSerializer.Serialize()呈現身體參數(我沒有嘗試過)。
  • 需要一些自定義代碼才能在枚舉值的日誌中獲得很好的描述。
  • StopWatch用法可以移動以在測量中包括反序列化。

這是一個基本完整的基類,例如使用(使用NLOG)登錄:

using System; 
using System.Diagnostics; 
using System.Linq; 
using NLog; 
using Newtonsoft.Json; 
using RestSharp; 

namespace Apis 
{ 
    public abstract class RestApiBase 
    { 
     protected readonly IRestClient _restClient; 
     protected readonly ILogger _logger; 

     protected RestApiBase(IRestClient restClient, ILogger logger) 
     { 
      _restClient = restClient; 
      _logger = logger; 
     } 

     protected virtual IRestResponse Execute(IRestRequest request) 
     { 
      IRestResponse response = null; 
      var stopWatch = new Stopwatch(); 

      try 
      { 
       stopWatch.Start(); 
       response = _restClient.Execute(request); 
       stopWatch.Stop(); 

       // CUSTOM CODE: Do more stuff here if you need to... 

       return response; 
      } 
      catch (Exception e) 
      { 
       // Handle exceptions in your CUSTOM CODE (restSharp will never throw itself) 
      } 
      finally 
      { 
       LogRequest(request, response, stopWatch.ElapsedMilliseconds); 
      } 

      return null; 
     } 

     protected virtual T Execute<T>(IRestRequest request) where T : new() 
     { 
      IRestResponse response = null; 
      var stopWatch = new Stopwatch(); 

      try 
      { 
       stopWatch.Start(); 
       response = _restClient.Execute(request); 
       stopWatch.Stop(); 

       // CUSTOM CODE: Do more stuff here if you need to... 

       // We can't use RestSharp deserialization because it could throw, and we need a clean response 
       // We need to implement our own deserialization. 
       var returnType = JsonConvert.DeserializeObject<T>(response.Content); 
       return returnType; 
      } 
      catch (Exception e) 
      { 
       // Handle exceptions in your CUSTOM CODE (restSharp will never throw itself) 
       // Handle exceptions in deserialization 
      } 
      finally 
      { 
       LogRequest(request, response, stopWatch.ElapsedMilliseconds); 
      } 

      return default(T); 
     } 

     private void LogRequest(IRestRequest request, IRestResponse response, long durationMs) 
     { 
      _logger.Trace(() => 
      { 
       var requestToLog = new 
       { 
        resource = request.Resource, 
        // Parameters are custom anonymous objects in order to have the parameter type as a nice string 
        // otherwise it will just show the enum value 
        parameters = request.Parameters.Select(parameter => new 
        { 
         name = parameter.Name, 
         value = parameter.Value, 
         type = parameter.Type.ToString() 
        }), 
        // ToString() here to have the method as a nice string otherwise it will just show the enum value 
        method = request.Method.ToString(), 
        // This will generate the actual Uri used in the request 
        uri = _restClient.BuildUri(request), 
       }; 

       var responseToLog = new 
       { 
        statusCode = response.StatusCode, 
        content = response.Content, 
        headers = response.Headers, 
        // The Uri that actually responded (could be different from the requestUri if a redirection occurred) 
        responseUri = response.ResponseUri, 
        errorMessage = response.ErrorMessage, 
       }; 

       return string.Format("Request completed in {0} ms, Request: {1}, Response: {2}", 
        durationMs, JsonConvert.SerializeObject(requestToLog), 
        JsonConvert.SerializeObject(responseToLog)); 
      }); 
     } 
    } 
} 

這個類將記錄是這樣的(漂亮的格式化粘貼在這裏):

Request completed in 372 ms, Request : { 
    "resource" : "/Event/Create/{hostId}/{startTime}", 
    "parameters" : [{ 
      "name" : "hostId", 
      "value" : "116644", 
      "type" : "UrlSegment" 
     }, { 
      "name" : "startTime", 
      "value" : "2016-05-18T19:48:58.9744911Z", 
      "type" : "UrlSegment" 
     }, { 
      "name" : "application/json", 
      "value" : "{\"durationMinutes\":720,\"seats\":100,\"title\":\"Hello StackOverflow!\"}", 
      "type" : "RequestBody" 
     }, { 
      "name" : "api_key", 
      "value" : "123456", 
      "type" : "QueryString" 
     }, { 
      "name" : "Accept", 
      "value" : "application/json, application/xml, text/json, text/x-json, text/javascript, text/xml", 
      "type" : "HttpHeader" 
     } 
    ], 
    "method" : "POST", 
    "uri" : "http://127.0.0.1:8000/Event/Create/116644/2016-05-18T19%3A48%3A58.9744911Z?api_key=123456" 
}, Response : { 
    "statusCode" : 200, 
    "content" : "{\"eventId\":2000045,\"hostId\":116644,\"scheduledLength\":720,\"seatsReserved\":100,\"startTime\":\"2016-05-18T19:48:58.973Z\"", 
    "headers" : [{ 
      "Name" : "Access-Control-Allow-Origin", 
      "Value" : "*", 
      "Type" : 3 
     }, { 
      "Name" : "Access-Control-Allow-Methods", 
      "Value" : "POST, GET, OPTIONS, PUT, DELETE, HEAD", 
      "Type" : 3 
     }, { 
      "Name" : "Access-Control-Allow-Headers", 
      "Value" : "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept", 
      "Type" : 3 
     }, { 
      "Name" : "Access-Control-Max-Age", 
      "Value" : "1728000", 
      "Type" : 3 
     }, { 
      "Name" : "Content-Length", 
      "Value" : "1001", 
      "Type" : 3 
     }, { 
      "Name" : "Content-Type", 
      "Value" : "application/json", 
      "Type" : 3 
     }, { 
      "Name" : "Date", 
      "Value" : "Wed, 18 May 2016 17:44:16 GMT", 
      "Type" : 3 
     } 
    ], 
    "responseUri" : "http://127.0.0.1:8000/Event/Create/116644/2016-05-18T19%3A48%3A58.9744911Z?api_key=123456", 
    "errorMessage" : null 
} 

希望你覺得這個有用!

+0

非常有用和緊湊 –

+0

我修改你的代碼是一個裝飾器到IRestClient接口,而不是基類,並使用log4net而不是NLOG。 log4net在寫出匿名類型的數組時需要更多的幫助,但否則它會很好。 –

+0

@DavidKeaveny真棒!我不熟悉將裝飾器添加到IRestClient接口,哪個類最終使用它?你有任何鏈接來檢查實施?另外檢查我是否添加了一個額外的項目符號點;)感謝您的評論。 – LucasMetal

0

作爲一個部分解決方案,您可以使用RESTClient實現的BuildUri方法:

var response = client.Execute(request); 
if (response.StatusCode != HttpStatusCode.OK) 
    throw new Exception($"Failed to send request: {client.BuildUri(request)}");