Search code examples
c#.net-6.0servicestack

Using ServiceStack, is it possible to create Derived Request class?


I have a situation where the only API offered to me returns many items and I want to make a derived request that will only return one item. Here's what my attempt looks like:

[Route("/v3/quotes/{stockTicker}")]
[DataContract]
public class Quotes_Request : BaseRequest<Quotes_Response, List<Quote>>
{
    [DataMember(Name = "stockTicker")]
    public string Symbol { get; init; }
    
    //Properties

    [DataMember(Name = "order")]
    public virtual SortOrder? Order { get; init; } = SortOrder.asc;

    [DataMember(Name = "limit")]
    public virtual int? Limit { get; init; } = 50_000;

    public Quotes_Request(string symbol) => Symbol = symbol;
}

public class Quote_Request : Quotes_Request
{
    [DataMember(Name = "order")]
    public override SortOrder? Order => SortOrder.desc;

    [DataMember(Name = "limit")]
    public override int? Limit => 1;

    public Quote_Request(string symbol, DateTime dateTime) : base(symbol)
    {
        TimestampLessThanOrEqual = dateTime;
    }
}

Returning the list of quotes using Quotes_Request works perfectly and I would have thought that my goal of returning a single quote with Quote_Request would work too, but there seems to be a problem because I get this error:

None of the given rest routes matches 'Quote_Request' request: /v3/quotes/{stockTicker}: Variable 'stockTicker' does not match any property.

To address the error, if I change the route on the parent class Quotes_Request to this:

[Route("/v3/quotes/{Symbol}")]
[DataContract]
public class Quotes_Request : BaseRequest<Quotes_Response, List<Quote>>
{
    [DataMember(Name = "stockTicker")]
    public string Symbol { get; init; }
    
    //truncated

The code runs but it's not correct because it's not using any of the DataMember attributes to name the query parameters, its using the property names. Which is actually the same thing it did with the Route. Unfortunately, I can't use the property names even if I wanted to because some of the DataMembers actually have a period in the name.

I did try one other thing, that was I tried making the Symbol property virtual so that I could override it in the Derived Quote_Request class like this:

public class Quote_Request : Quotes_Request
{
    [DataMember(Name = "stockTicker")]
    public override string Symbol { get; init; }

    //truncated

But that didn't work either. I got the same error as in the first place:

None of the given rest routes matches 'Quote_Request' request: /v3/quotes/{stockTicker}: Variable 'stockTicker' does not match any property.

I'm kinda surprised that it didn't pick up the DataMember name even when it was overriden but if that would have worked I would have just overriden all the properties.

Is there anything else I can try? Did I miss something? Or is this something that will need to be fixed in ServiceStack?


Solution

  • Request DTOs must be unique and can only implement a single API.

    To share code amongst Request DTOs you can have them implement the same abstract base class.

    However we'd typically recommend to avoid using inheritance in Request DTOs since they represent declarative definition of your Service Contract and using inheritance hides their intent.