C# RestSharp 打印原始请求和响应标头
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15683858/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
RestSharp print raw request and response headers
提问by Professor Chaos
I'm using RestSharp
to make calls to a webservice. All is well but I was wondering if it would be possible to print the raw request headers and body that is sent out and the raw response headers and the response body that comes back.
我正在使用RestSharp
调用网络服务。一切都很好,但我想知道是否可以打印发出的原始请求标头和正文以及返回的原始响应标头和响应正文。
This is my code where I create a request and get a response back
这是我创建请求并获得响应的代码
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;
}
so, Would it be possible to print the raw request and response?
那么,是否可以打印原始请求和响应?
回答by Jiongfeng Luo
I just found the code below in the RestSharp examples. It allows you to print your raw response.
我刚刚在 RestSharp 示例中找到了下面的代码。它允许您打印原始响应。
client.ExecuteAsync(request, response =>
{
Console.WriteLine(response.Content);
});
回答by oleksa
You can try to use
你可以尝试使用
Trace.WriteLine(request.JsonSerializer.Serialize(request));
to get request and
获取请求和
response.Content(); // as Luo have suggested
request is not the same, as Fiddler shows but it contains all data and is readable (with some RestSharp garbage at the end).
request 不一样,如 Fiddler 所示,但它包含所有数据并且是可读的(最后有一些 RestSharp 垃圾)。
回答by akava
.net provides its own yet powerful logging feature. This can be turned on via config file.
.net 提供了自己但强大的日志记录功能。这可以通过配置文件打开。
I found this tip here. John Sheehan pointed to How to: Configure Network Tracingarticle. (a note: I edited the config provided, turned off unnecessary (for me) low level logging).
我在这里找到了这个提示。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>
回答by The Muffin Man
You have to loop through the request.Parameters
list and format it to a string in whatever format you like.
您必须遍历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();
If you want the output to show request headers and then the body similar to Fiddler, you just need to order the collection by Request headers and then by Request body. The Parameter
object in the collection has a Type
parameter enum.
如果您希望输出显示请求标头,然后显示类似于 Fiddler 的正文,则只需按请求标头然后按请求正文对集合进行排序。Parameter
集合中的对象有一个Type
参数枚举。
回答by LucasMetal
RestSharp doesn't provide a mechanism to achieve exactly what you want and activating the .Net tracing is a bit overkilling IMO.
RestSharp 没有提供一种机制来实现您想要的功能,并且激活 .Net 跟踪对 IMO 来说有点矫枉过正。
For logging (debugging) purposes (something that I can leave turned on for a while in PROD for example) I have found this approach to be very useful (although it has some details on how to call it, read below the code):
对于日志记录(调试)目的(例如,我可以在 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)));
}
Things to note:
注意事项:
- Headers, Url segments, QueryString parameters, body, etc. all are considered parameters for RestSharp, all that appear in the parameters collection of the request, with their corresponding type.
- The log method must be called AFTER the request took place. This is needed because of the way RestSharp works, the Execute method will add headers, run the authenticators (if some configured), etc. and all these will modify the request. So in order to have the all the real parameters sent logged, the Execute method should have been called before logging the request.
- RestSharp itself will never throw (instead errors get saved in the response.ErrorException property), but I think deserialization could throw (not sure) and besides I needed to log the raw response, so I chose to implement my own deserialization.
- Have in mind that RestSharp uses its own formatting when converting parameters values to generate the Uri, so serializing the parameters to log them may not show the exact same things that were put in the Uri. That's why the
IRestClient.BuildUri
method it's pretty cool to get the actually called Uri (including the base url, the replaced url segments, the added queryString parameters, etc). - EDIT:Also have in mind that it could happen that the serializer RestSharp is using for the body is not the same this code is using, so I guess code could be adjusted to use
request.JsonSerializer.Serialize()
for rendering the body parameter (I haven't tried this). - Some custom code was needed to achieve nice descriptions in the logs for the enums values.
StopWatch
usage could be moved around to include deserialization in the measuring.
- Headers、Url 段、QueryString 参数、body 等都被认为是 RestSharp 的参数,都出现在请求的参数集合中,具有对应的类型。
- 必须在请求发生后调用 log 方法。由于 RestSharp 的工作方式,Execute 方法将添加标头,运行身份验证器(如果已配置)等,因此需要这样做,所有这些都将修改请求。因此,为了记录所有发送的真实参数,应该在记录请求之前调用 Execute 方法。
- RestSharp 本身永远不会抛出(而是将错误保存在 response.ErrorException 属性中),但我认为反序列化可能会抛出(不确定),而且我需要记录原始响应,所以我选择实现我自己的反序列化。
- 请记住,RestSharp 在转换参数值以生成 Uri 时使用其自己的格式,因此序列化参数以记录它们可能不会显示与 Uri 中放置的完全相同的内容。这就是为什么
IRestClient.BuildUri
获取实际调用的 Uri(包括基本 url、替换的 url 段、添加的 queryString 参数等)的方法非常酷的原因。 - 编辑:还请记住,用于主体的序列化程序 RestSharp 可能与此代码使用的不一样,所以我想可以调整代码以
request.JsonSerializer.Serialize()
用于呈现主体参数(我还没有尝试过) . - 需要一些自定义代码才能在日志中对枚举值进行很好的描述。
StopWatch
可以移动用法以在测量中包括反序列化。
Here it is a basic complete base class example with logging (using NLog):
这是一个基本的完整基类示例,带有日志记录(使用 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));
});
}
}
}
This class will log something like this (pretty formatted for pasting here):
这个类将记录如下内容(格式非常适合粘贴在这里):
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
}
Hope you find this useful!
希望你觉得这个有用!
回答by starteleport
As a partial solution, you can use RestClient's BuildUri
method:
作为部分解决方案,您可以使用 RestClient 的BuildUri
方法:
var response = client.Execute(request);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Failed to send request: {client.BuildUri(request)}");
回答by giganoide
An option is use your own authenticator. RestSharp allows to inject an authenticator:
一个选项是使用您自己的身份验证器。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
}
}
The authenticator's Authenticate method is the very first thing called upon calling RestClient.Execute or RestClient.Execute. The Authenticate method is passed the RestRequest currently being executed giving you access to every part of the request data (headers, parameters, etc.) from RestSharp's wiki
身份验证器的 Authenticate 方法是在调用 RestClient.Execute 或 RestClient.Execute 时首先调用的方法。将 Authenticate 方法传递给当前正在执行的 RestRequest,让您可以访问来自 RestSharp 维基的请求数据的每一部分(标头、参数等)
This means than in the Authenticate method you can log the request.
这意味着您可以在 Authenticate 方法中记录请求。