RestSharp打印原始请求和响应标头

时间:2013-03-28 14:00:33

标签: c# 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;
}

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

7 个答案:

答案 0 :(得分:48)

正如我们已经知道的那样,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)));
}

需要注意的事项:

  • 标题,Url段,QueryString参数,正文等都被视为RestSharp的参数,所有这些参数都出现在请求的参数集合中,并且具有相应的类型。
  • 必须在请求发生后调用日志方法。这是必需的,因为RestSharp的工作方式,Execute方法将添加标头,运行验证器(如果配置了一些)等等,所有这些都将修改请求。因此,为了记录发送的所有实际参数,应在记录请求之前调用Execute方法。
  • RestSharp本身永远不会抛出(而是将错误保存在response.ErrorException属性中),但我认为反序列化可能会抛出(不确定),而且我需要记录原始响应,所以我选择实现自己的反序列化。
  • 请记住,在转换参数值以生成Uri时,RestSharp会使用自己的格式,因此序列化参数以记录它们可能无法显示与Uri完全相同的内容。这就是IRestClient.BuildUri方法获得实际调用的Uri(包括基本URL,替换的url段,添加的queryString参数等)非常酷的原因。
  • 编辑:还要记住,可能会发生串行器RestSharp正在使用的主体与此代码使用的不一样,所以我猜代码可以调整为使用{{1用于渲染body参数(我没试过这个)。
  • 需要一些自定义代码才能在日志中为枚举值实现更好的描述。
  • 可以移动
  • request.JsonSerializer.Serialize()使用以包括测量中的反序列化。

这是一个基本的完整基类示例,带有日志记录(使用NLog):

StopWatch

这个类会记录这样的东西(格式很适合粘贴):

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));
            });
        }
    }
}

希望你觉得这很有用!

答案 1 :(得分:24)

.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>

答案 2 :(得分:5)

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

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

答案 3 :(得分:5)

您必须遍历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的正文,您只需按Request标题排序集合,然后按Request body排序。集合中的Parameter对象具有Type参数枚举。

答案 4 :(得分:1)

一个选项是使用您自己的身份验证器。 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   使您可以访问请求数据的各个部分(标题,   参数等)   from RestSharp's wiki

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:-2)

您可以尝试使用

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

获取请求和

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

请求与Fiddler显示的不一样,但它包含所有数据且可读(最后有一些RestSharp垃圾)。