[英]RestSharp print raw request and response headers
我正在使用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;
}
那么,是否可以打印原始請求和響應?
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)));
}
注意事項:
IRestClient.BuildUri
方法獲取實際調用的 Uri 非常酷(包括基本 url、替換的 url 段、添加的 queryString 參數等)。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
}
希望你覺得這個有用!
.net 提供了自己但強大的日志記錄功能。 這可以通過配置文件打開。
我在 這里找到了這個提示。 John Sheehan 指出如何:配置網絡跟蹤文章。 (注意:我編輯了提供的配置,關閉了不必要的(對我而言)低級日志記錄)。
<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>
我剛剛在 RestSharp 示例中找到了下面的代碼。 它允許您打印原始響應。
client.ExecuteAsync(request, response =>
{
Console.WriteLine(response.Content);
});
您必須遍歷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();
如果您希望輸出顯示請求標頭,然后顯示類似於 Fiddler 的正文,則只需按請求標頭然后按請求正文對集合進行排序。 集合中的Parameter
對象有一個Type
參數枚舉。
您可以使用Fiddler來捕獲 HTTP 請求。
一個選項是使用您自己的身份驗證器。 RestSharp 允許注入身份驗證器:
var client = new RestClient();
client.Authenticator = new YourAuthenticator(); // implements IAuthenticator
public interface IAuthenticator
{
void Authenticate(IRestClient client, IRestRequest request);
}
internal class YourAuthenticator: IAuthenticator
{
public void Authenticate(IRestClient client, IRestRequest request)
{
// log request
}
}
身份驗證器的 Authenticate 方法是在調用 RestClient.Execute 或 RestClient.Execute 時首先調用的方法。 將 Authenticate 方法傳遞給當前正在執行的 RestRequest,讓您可以訪問來自 RestSharp 維基的請求數據的每一部分(標頭、參數等)
這意味着您可以在 Authenticate 方法中記錄請求。
作為部分解決方案,您可以使用 RestClient 的BuildUri
方法:
var response = client.Execute(request);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Failed to send request: {client.BuildUri(request)}");
如果您只想查看原始響應,請嘗試覆蓋反序列化器(大部分內容是從restsharp 107.3.0復制粘貼的):
using RestSharp.Serializers;
// …
public class StupidLogSerialiser : IRestSerializer, ISerializer, IDeserializer {
public string Serialize(object obj) => null;
public string Serialize(Parameter bodyParameter) => Serialize(null);
public T Deserialize<T>(RestResponse response) {
Console.WriteLine(response.Content);
return default(T);
}
public string ContentType { get; set; } = "application/json";
public ISerializer Serializer => this;
public IDeserializer Deserializer => this;
public DataFormat DataFormat => DataFormat.Json;
public string[] AcceptedContentTypes => RestSharp.Serializers.ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => contentType.EndsWith("json", StringComparison.InvariantCultureIgnoreCase);
}
// …
client.UseSerializer(() => new StupidLogSerialiser());
我知道這篇文章已經有將近 10 年的歷史了,對此感到抱歉。 您現在可以在發送請求之前和收到響應之后指定委托。
這就是我實現這一特殊需求的方式。
public class TraceRequest : RestRequest
{
#region Properties
private bool TraceStreamContent { get; set; }
#endregion
#region Constructor
public TraceRequest(string pResource, bool pTraceStreamContent)
: base(pResource)
{
this.TraceStreamContent = pTraceStreamContent;
this.InitializeLogs();
}
#endregion
#region Methods
private void InitializeLogs()
{
this.OnBeforeRequest = this.OnBeforeRequestMethod;
this.OnAfterRequest = this.OnAfterRequestMethod;
}
private ValueTask OnBeforeRequestMethod(HttpRequestMessage pMessage)
{
var builder = new StringBuilder();
builder.AppendLine("------------------------------");
builder.AppendLine("REQUEST [{0}] {1}", pMessage.Method, pMessage.RequestUri);
foreach (var header in pMessage.Headers)
{
builder.AppendLine(" {0}: {1}", header.Key, string.Join(';', header.Value));
}
if (this.TraceStreamContent)
{
var stream = pMessage.Content.ReadAsStream();
this.ReadStream(stream, builder);
}
else
{
this.ReadContent(pMessage.Content, builder);
}
builder.AppendLine("------------------------------");
var content = builder.ToString();
Console.WriteLine(content);
return ValueTask.CompletedTask;
}
private void ReadContent(HttpContent pContent, StringBuilder pBuilder)
{
foreach (var header in pContent.Headers)
{
pBuilder.AppendLine(" {0}: {1}", header.Key, string.Join(';', header.Value));
}
this.ReadContent(pContent as StreamContent, pBuilder);
this.ReadContent(pContent as StringContent, pBuilder);
this.ReadContent(pContent as MultipartFormDataContent, pBuilder);
Console.WriteLine();
}
private void ReadContent(MultipartFormDataContent pContent, StringBuilder pBuilder)
{
if (pContent != null)
{
foreach (var content in pContent)
{
pBuilder.AppendLine();
this.ReadContent(content, pBuilder);
}
}
}
private void ReadContent(StreamContent pContent, StringBuilder pBuilder)
{
if (pContent != null)
{
var stream = pContent.ReadAsStream();
pBuilder.AppendLine(" contains {0} bytes", stream.Length);
}
}
private void ReadContent(StringContent pContent, StringBuilder pBuilder)
{
if (pContent != null)
{
var stream = pContent.ReadAsStream();
pBuilder.Append(" ");
this.ReadStream(stream, pBuilder);
}
}
private void ReadStream(Stream pStream, StringBuilder pBuilder)
{
var index = 0L;
var length = pStream.Length;
var buffer = new byte[1024];
while (index < length - 1)
{
var read = pStream.Read(buffer, 0, 1024);
var result = Encoding.UTF8.GetString(buffer, 0, read);
pBuilder.Append(result);
index += read;
}
pBuilder.AppendLine();
pStream.Seek(0L, SeekOrigin.Begin);
}
private ValueTask OnAfterRequestMethod(HttpResponseMessage pMessage)
{
var builder = new StringBuilder();
builder.AppendLine("------------------------------");
builder.AppendLine("RESPONSE {2} [{0}] {1}", pMessage.RequestMessage.Method, pMessage.RequestMessage.RequestUri, pMessage.StatusCode);
foreach (var header in pMessage.Headers)
{
builder.AppendLine(" {0}: {1}", header.Key, string.Join(';', header.Value));
}
if (this.TraceStreamContent)
{
var stream = pMessage.Content.ReadAsStream();
this.ReadStream(stream, builder);
}
else
{
this.ReadContent(pMessage.Content, builder);
}
builder.AppendLine("------------------------------");
var content = builder.ToString();
Console.WriteLine(content);
return ValueTask.CompletedTask;
}
#endregion
}
您現在可以像這樣使用 class TraceRequest,boolean TraceStreamContent 將啟用完整的 HTTP 跟蹤。 發送附件中的文件時可能太大。
var client = new RestClient("https://dev.test.cloud:4511")
{
Authenticator = new HttpBasicAuthenticator("user", "password")
};
var request = new TraceRequest("test", true)
.AddJsonBody(fax)
.AddFile("first", new byte[] { 65, 66, 67, 68 }, "first.txt")
.AddFile("second", new byte[] { 69, 70, 71, 72 }, "second.txt");
var response = client.Post(request);
希望這會幫助別人!
你可以嘗試使用
Trace.WriteLine(request.JsonSerializer.Serialize(request));
獲取請求和
response.Content(); // as Luo have suggested
request 不一樣,如 Fiddler 所示,但它包含所有數據並且是可讀的(最后有一些 RestSharp 垃圾)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.