RestSharp 打印原始请求和响应头

我正在使用 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;
}

那么,是否可以打印原始请求和响应?

76746 次浏览

我刚刚在 RestSharp 示例中找到了下面的代码,它允许您打印原始响应。

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

你可以试试

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

接到请求

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

Request 不一样,如 Fiddler 所示,但它包含所有数据并且是可读的(末尾有一些 RestSharp 垃圾)。

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>

可以使用 小提琴手捕获 HTTP 请求。

您必须遍历 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参数枚举。

RestSharp 没有提供一种机制来实现您想要的结果并激活。网络跟踪有点过头了 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)));
}

注意事项:

  • Header、 Url 段、 QueryString 参数、 body 等等都被认为是 RestSharp 的参数,所有这些都出现在请求的参数集合中,并具有相应的类型。
  • 日志方法必须在请求发生后调用。这是必要的,因为 RestSharp 的工作方式,Execute 方法将添加头文件,运行验证器(如果配置了一些) ,等等,所有这些都将修改请求。因此,为了记录发送的所有实际参数,应该在记录请求之前调用 Execute 方法。
  • RestSharp 本身永远不会抛出(而是在响应中保存错误)。但是我认为反序列化可能会抛出(不确定) ,而且我需要记录原始响应,所以我选择实现自己的反序列化。
  • 请记住,RestSharp 在转换参数值以生成 Uri 时使用自己的格式,因此序列化参数以记录它们可能不会显示与放在 Uri 中完全相同的内容。这就是为什么 IRestClient.BuildUri方法可以获得实际调用的 Uri (包括基 URL、替换的 url 段、添加的 queryString 参数等)。
  • 编辑: 还要记住,可能发生的情况是,RestSharp 在 body 中使用的序列化程序与这段代码使用的不一样,所以我猜测可以调整代码,使用 request.JsonSerializer.Serialize()来渲染 body 参数(我还没有试过这个)。
  • 需要一些自定义代码来实现枚举值在日志中的良好描述。
  • 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
}

希望你觉得这有用!

作为部分解决方案,您可以使用 RestClient 的 BuildUri方法:

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

一个选项是使用您自己的身份验证器。 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 时 向当前正在执行的 RestRequest 传递身份验证方法 允许您访问请求数据的每个部分(头, 参数等) 来自 RestSharp 的维基百科

这意味着可以在 Authenticate 方法中记录请求。

如果您只想查看原始响应,那么尝试覆盖反序列化器(大部分拷贝是从 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());