Reputation: 9070
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.
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?
Upvotes: 90
Views: 96009
Reputation: 4503
Fiddler was already mentioned in one answer but with a few lines of PowerShell, you can create a temporary HTTP listener, see here.
The following code will wait for a request in port 9876 and will output the raw request to console:
$httpListener = New-Object System.Net.HttpListener
$httpListener.Prefixes.Add("http://localhost:9876/")
$httpListener.Start()
$context = $httpListener.GetContext()
# will wait here, until a request is sent
$requestBodyReader = New-Object System.IO.StreamReader $context.Request.InputStream
$requestBodyReader.ReadToEnd()
$httpListener.Stop()
Upvotes: 0
Reputation: 914
I know this post is almost 10 years old, sorry about that. You can now specify delegates just before sending the request, and just after receiving the response.
This is how I've implemented this particular need.
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
}
You can now use the class TraceRequest like this, the boolean TraceStreamContent will enable a complete HTTP tracing. Might be too big when sending files in attachment.
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);
Hope this will help someone !
EDIT
Here is the missing extension method for StringBuilder
public static void AppendLine(this StringBuilder pBuilder, string pFormat, params object[] pArgs)
{
pBuilder.AppendFormat(pFormat, pArgs);
pBuilder.AppendLine();
}
Upvotes: 3
Reputation: 4740
If you just want to take a look at the raw response, try overriding the deserialiser (most of this copy-pasted from 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());
Upvotes: 0
Reputation: 1393
RestSharp doesn't provide a mechanism to achieve exactly what you want and activating the .Net tracing is a bit overkilling 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):
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:
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).request.JsonSerializer.Serialize()
for rendering the body parameter (I haven't tried this).StopWatch
usage could be moved around to include deserialization in the measuring. Here it is a basic complete base class example with logging (using 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!
Upvotes: 99
Reputation: 19
An option is use your own authenticator. RestSharp allows to inject an authenticator:
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
This means than in the Authenticate method you can log the request.
Upvotes: 0
Reputation: 1269
As a partial solution, you can use RestClient's BuildUri
method:
var response = client.Execute(request);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Failed to send request: {client.BuildUri(request)}");
Upvotes: 1
Reputation: 20004
You have to loop through the request.Parameters
list and format it to a string in whatever format you like.
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.
Upvotes: 8
Reputation: 560
.net provides its own yet powerful logging feature. This can be turned on via config file.
I found this tip here. John Sheehan pointed to How to: Configure Network Tracing article. (a note: I edited the config provided, turned off unnecessary (for me) low level logging).
<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>
Upvotes: 31
Reputation: 4036
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).
Upvotes: -3
Reputation: 141
I just found the code below in the RestSharp examples. It allows you to print your raw response.
client.ExecuteAsync(request, response =>
{
Console.WriteLine(response.Content);
});
Upvotes: 7