2
votes

I am trying to make a simple api post call using servicestack and it keeps throwing an exception "not found". When the same post call is made directly to the api using a web browser rest api e.g. postman, the api call works.

I have decorated my request object with the route attributes

[Route("/register", "POST")]
public class Register : IReturn<RegistrationResponse>
{
    public DateTime? BirthDate { get; set; }

    public string Continue { get; set; }

    public string Email { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string Gender { get; set; }

    public string Password { get; set; }
}

The JsonServiceClient is initialised with the base uri but the following call fails

_client = new JsonServiceClient(_apiUri);
_client.HttpMethod = HttpMethods.Post;
var response = _client.Send(body);

The exception that I catch is:

$exception {"Not Found"} System.Exception {ServiceStack.ServiceClient.Web.WebServiceException} at ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowWebServiceException[TResponse](Exception ex, String requestUri) at ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowResponseTypeException[TResponse](Object request, Exception ex, String requestUri) at ServiceStack.ServiceClient.Web.ServiceClientBase.HandleResponseException[TResponse](Exception ex, Object request, String requestUri, Func1 createWebRequest, Func2 getResponse, TResponse& response) at ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](Object request) at ApiService`2.Post(String path, TParams body) in ApiService.cs:line 81

The documentation on the new API at servicestack mentions the use of the Route attributes decorating the request DTO and the use of the IReturn but from looking at the code behind the Send method, it is working out the rest api url from the name of the request, which implies that your request dto cannot be named anything different.

public virtual TResponse Send<TResponse>(object request)
{
    var requestUri = this.SyncReplyBaseUri.WithTrailingSlash() + request.GetType().Name;
    var client = SendRequest(requestUri, request);

    try
    {
        var webResponse = client.GetResponse();
        return HandleResponse<TResponse>(webResponse);
    }
    catch (Exception ex)
    {
        TResponse response;

        if (!HandleResponseException(ex,
            request,
            requestUri,
            () => SendRequest(HttpMethods.Post, requestUri, request),
            c => c.GetResponse(),
            out response))
        {
            throw;
        }

        return response;
    }
}

What is causing the Not Found exception?

2

2 Answers

1
votes

Everthing in your Register class looks correct.

For your client call I would change it to

_client = new JsonServiceClient(_apiUri);
_client.Post(new Register()); //assuming you can map your 'body' variable to a Register class

Just to lose the extra line of code.

it is working out the rest api url from the name of the request, which implies that your request dto cannot be named anything different.

It is working out the endpoint that the rest api will hit. Once it hits the endpoint, the internals of ServiceStack should handle the routing based on the Operation (in this case Register) and Http method. Basically it will try to find a Service class (any class inheriting the Service marker interface) that has the request object (Register) as a parameter and it will use the Http method as the 'function' to call.

What is causing the Not Found exception?

Not exactly sure about this. If you could provide your 'Service' class it may help.

If you have a Service class like

public class RegisterService : Service
{
    public RegistrationResponse Post(Register request) 
    {
        //service code 
        return new RegistrationResponse();
    }
}

the routing should work.

0
votes

The fix for this was to ensure that the servicestack feature for predefined routes was enabled on the api. Once this is done, you don't need to bother with the Route attribute on the request objects.

The end point host config now looks like this:

new EndpointHostConfig
{
    DefaultContentType = ContentType.Json,
    EnableFeatures = Feature.None
                            .Add(Feature.Json)
                            .Add(Feature.PredefinedRoutes),
    GlobalResponseHeaders = new Dictionary<string, string>(),
    DefaultRedirectPath = "/documentation"
}