I am passing calls to an external service using an implementation of IRestGateway, which a generic Send method to perform the GET/POST/PUT/DELETE calls. Everything is working great.
Now, I would now like to improve my ResponseDto by adding IHasResponseStatus to the definition.
However, once I do that the generic Send method in my IRestGateway implementation fails,
because the fromJson conversion is using the type QueryTradeResponse, when I need it to
be using the type of the QueryTradeResponse.Result.
Similar examples I have found (such as Northwind OrderService) are not using generics, so they do not have the same problem, as they hard code the Result type
Is there a way to handle this whilst still using generics?
namespace My.ServiceModel
[Route("/trade", "GET")]
public class QueryTrade : IGet, IReturn<QueryTradeResponse>
{
// nothing here
}
public class QueryTradeResponse : IHasResponseStatus
{
public List<Trade> Result { get; set; } = = new List<Trade>();
public ResponseStatus ResponseStatus { get; set; } = new ResponseStatus();
}
namespace My.ServiceInterface
{
public class TradeGateway : IRestGateway
public T Send<T>(IReturn<T> request, string method, bool sendRequestBody = true, string idempotencyKey = null)
{
var relativeUrl = request.ToUrl(method);
var body = sendRequestBody ? request.GetDto().ToJson() : null;
// json is equivalent of List<Trade>
var json = Send(relativeUrl, method, body, idempotencyKey);
// this is QueryTradeResponse
var ret = typeof(T).Name;
// how do I assign the json to the QueryTradeResponse.Result?
var response = json.FromJson<T>();
return response;
}
namespace My.Tests.IntegrationTests
public IServiceClient CreateClient() => new JsonServiceClient(BaseUri);
[Test]
public void Query_Trade()
{
var client = CreateClient();
try
{
var response = client.Get(new QueryTrade { });
Assert.That(response, Is.Not.Null);
}
catch (WebServiceException webEx)
{
var msg = String.Format("Should not fail with {0} error", webEx.StatusCode);
Assert.Fail(msg);
}
}
The JSON must match absolutely the schema you’re trying to deserialize into if you want typed deserialization (that’s the purpose of DTOs) otherwise you can use JSON.parse() to deserializes the JSON into generic dictionary and access the results that way.
Why can’t you change the IReturn<T> Type to match the actual response? That’s its purpose, e.g. use List<T> if that’s what’s being returned. I don’t understand why it’s different, all C# Service Clients use generic APIs to send different Request DTOs where it’s able to deserialize the response by using its IReturn type.
Fair enough, but the external services know nothing about ResponseStatus (a ServiceStack data type). So, how can the JSON string returned from the REST call
“match absolutely the schema you’re trying to deserialize”
, when the response DTO implements IHasResponseStatus?
I have over 100 external endpoints to call, so using IRestGateway is a great way to reduce code, as I only need to define request/response DTOs and endpoint routing, and let the generic Send methods handle the communication.
I am sure ServiceStack has this covered, and probably my description is lacking clarity.
I’m confused, if the external services you’re calling knows nothing about the ResponseStatus then it’s unlikely to be returning a serialized ResponseStatus on Error? So there’s no reason to document the Response DTO to have one if the API is not going to be returning one on error.
Note: when calling ServiceStack Services, if your Response DTO does not have a ResponseStatus property then ServiceStack will throw a generic ErrorResponse DTO so you can still have ServiceStack Services returning naked lists, i.e. List<T> and it will still throw a generic ErrorResponse DTO with a populated ResponseStatus property, but you’re not going to get the same structured Exception handling for external APIs that don’t return a serialized ResponseStatus like ServiceStack APIs do.
If I have manged to confuse you, then I dread to think how wrong I must be.
Your latest response describes exactly what I was trying to previously articulate.
I am trying to benefit from structured Exception handling (made available using ResponseStatus). Such that, when my ServiceStack gateway receives an error from the external service (e.g. HTTP 404, 500, etc), rather than having my gateway throwing a generic ErrorResponseDto, it could inject information into a ResponseStatus to help my ServiceStack client understand the reasons.
However, I think I have misunderstood the purpose of ResponseStatus, and it is only relevant for ServiceStack services and not appropriate for capturing errors from external services.