Say I want to create a C# implementation of an API for some 3rd party service, and I want to have an option to get the json posts and responses out somehow, what would be an elegant way of doing that?
Example of a method:
public class MyApiImplementation{
public PaymentRespose CreatePayment(decimal amount, string orderId){
//some code here
}
}
When I call that method (compiled), I want to get the json form the underlying rest response, so I can debug it or log it.
I don't want to do something like this...:
public class MyApiImplementation{
public PaymentRespose CreatePayment(decimal amount, string orderId, out string json){
//some code here
}
}
When I call the api, I want to be able to get the json out somehow.
public api = new MyApiImplementation();
PaymentResponse pr = api.CreatePayment(100M, "ORDER1234");
string json = ???
I could include the json in the PaymentResponse, but... It seems like it does not belong there.
I am thinking an optional logger of some sort in a contructor overload.
Any good suggestions on how to implement this? What could it look like?
Edit:
Followup
I don't think I was clear enough in my description, so let me try to elaborate and provide a bit more code.
Purpose
I am creating a library class, which is a C# wrapper around a REST interface for a payment provider. I will provide this library to developers via Nuget.
This is what I do today
public class AwesomePaymentClient{
public Payment CreatePayment(string orderId, string currency, out string json)
{
var request = CreatePostRequest("payments");
request.AddParameter("currency", currency);
request.AddParameter("order_id", orderId);
var response = Client.Execute<Payment>(request);
json = response.Content;
VerifyResponse(response);
return response.Data;
}
//all other methods omitted from example
}
I don't particularly like the
out string jsonbit, but I would like developers to have a way to get the actual json response for their own debugging/logging purposes.
I could create an overload that does not have the out parameter, but I feel it makes the API kind of dirty - and that it is a separate concern.
I could also include a json property on Payment-class, but again I would like to keep it separate.
So I am thinking, maybe I make a contructor overload, that takes in some kind of logger or... Something, that I can't quite figure out what is.
Middleware etc is out of the question, as I am just providing a library.
Hope this helps in making my intentions clearer. :)
2 Answers 2
With concurrent access to an instance, the JSON response cannot be stored in a property (because a different request might overwrite its value before it is read).
With a "logger", it is still necessary to somehow find out which JSON response belongs to which request, so you'd need some identifier.
I'd prefer to offer two versions of the interface: a "normal", and an "extended" one, like:
public interface IPaymentClient
{
IPayment CreatePayment(string orderId, string currency);
}
public interface IPaymentClientEx: IPaymentClient
{
IPayment CreatePayment(string orderId, string currency, out string json);
}
With implementations like
public class AwesomePaymentClient: IPaymentClient
{
public IPayment CreatePayment(string orderId, string currency)
{
var request = CreatePaymentRequest(orderId, currency);
var response = ExecutePaymentRequest(request);
return response.Data;
}
protected IRequest CreatePaymentRequest(string orderId, string currency)
{
var request = CreatePostRequest("payments");
request.AddParameter("currency", currency);
request.AddParameter("order_id", orderId);
return request;
}
protected IResponse ExecutePaymentRequest(IRequest request)
{
var response = Client.Execute<Payment>(request);
VerifyResponse(response);
return response;
}
}
public class AwesomePaymentClientEx: AwesomePaymentClient, IPaymentClientEx
{
public IPayment CreatePayment(string orderId, string currency, out string json)
{
var request = CreatePaymentRequest(orderId, currency);
var response = ExecutePaymentRequest(request);
json = response.Content;
return response.Data;
}
}
Now the developer can choose which version he needs. It is not perfectly clean, but I guess it comes closer to your requirements. And the "extended" version still offers the "normal" functions also.
-
I like this approach. I don't think there is a cleaner way to accomplish this. :)Kjensen– Kjensen2017年11月09日 14:41:49 +00:00Commented Nov 9, 2017 at 14:41
The API should include a logger interface for each method that is being exposed. Maybe something like this:
public IPayment CreatePayment(string orderId, string currency, ILogger logger)
Then each consumer of the API could implement the logger as they see fit passing in a logger implementation.
In the method call, simply add (or similar):
Logger.LogResponse(string response);
Then the logging implemention will do the logging to file, db, etc. The consumer of your API will will decide. Internally handle the conversion/serialization of the JSON as you see fit.
You provide the interface, your API consumers implement. If there was no implantation passed (null), you could have an internal implementation that uses System.Diagnostics or similar.
//some code here
bit that you've omitted - that is where any such logging would go :)