1

I'm querying a ServiceStack service that I recently had to muck with to get the POST action working, and now when I call my GET action, the JSON object is no longer being passed in, and for the life of me I can't figure out what I did to break it...

Here's my request headers:

Request Url: http://internalserver:8181/citations
Request Method: GET
Status Code: 200
Params: {}

Here's my Configure in Global:

    public override void Configure(Container container)
    {
        container.RegisterAutoWired<CitationRequest>();
        container.RegisterAutoWired<Citation>();

        //Not sure I need these usings...?
        using (var addCitation = container.Resolve<CitationService>())
        {
            addCitation.Post(container.Resolve<CitationRequest>());
            addCitation.Get(container.Resolve<CitationRequest>());
            addCitation.Delete(container.Resolve<CitationRequest>());
        }

        Plugins.Add(new CorsFeature());
        RequestFilters.Add((httpReq, httpRes, requestDto) =>
        {
            if (httpReq.HttpMethod == "OPTIONS")
                httpRes.EndRequestWithNoContent(); //   extension method                    
        });

        SetConfig(new EndpointHostConfig
        {
            DefaultContentType = ContentType.Json,
            ReturnsInnerException = true,
            DebugMode = true,
            AllowJsonpRequests = true,
            ServiceName = "SSD Citations Web Service",
            WsdlServiceNamespace = "http://www.servicestack.net/types",
            WriteErrorsToResponse = true
        });
    }

Here's my Service:

[Route("/citations/{ReportNumber}/{ReportNumber_Prefix}/{AgencyId}", "GET, DELETE, OPTIONS")]
[Route("/citations", "GET, POST, DELETE, OPTIONS")]
public class CitationRequest : IReturn<CitationResponse>
{
    public string ReportNumber { get; set; }
    public int ReportNumber_Prefix { get; set; }
    public string AgencyId { get; set; }
    public DateTime ViolationDateTime { get; set; }
    public CitationStatus Status { get; set; }
}
public class CitationResponse
{
    public bool Accepted { get; set; }
    public string ActivityId { get; set; }
    public int ParticipantId { get; set; }
    public string Message { get; set; }
    public Exception RmsException { get; set; }
}
public class CitationService : Service
{
    public Repository Repository { get { return new Repository(); } }
    public CitationResponse Get(Citation citation)
    {
        var isDuplicate = Repository.IsDuplicateReportNumber(citation.AgencyId, citation.ReportNumber, citation.ReportNumber_Prefix);
        var citationResponse = new CitationResponse
           {
                Accepted = isDuplicate,
                Message = isDuplicate ? "Report Number already exists in db." : "Report Number has not yet been used."
            };
        return citationResponse;
    }

    public CitationResponse Post(CitationRequest request)
    {
        var response = new CitationResponse { Accepted = false };
        if (string.IsNullOrEmpty(request.ReportNumber))
        {
            response.Accepted = false;
            response.Message = "No data sent to service.  Please enter data in first.";
            return response;
        }
        try
        {
            response.ActivityId = Repository.CreateCitation(request.ReportNumber,     request.ReportNumber_Prefix, request.ViolationDateTime, request.AgencyId, request.Status);
            response.Accepted = true;
        }
        catch (Exception ex)
        {
            response.Accepted = false;
            response.Message = ex.Message;
        }
        return response;
    }

    public CitationResponse Delete(CitationRequest citation)
    {
        var citationResponse = new CitationResponse();
        try
        {
            if (Repository.DeleteCitation(citation.ReportNumber, citation.AgencyId, citation.ReportNumber_Prefix))
            {
                citationResponse.Accepted = true;
                citationResponse.Message = "Citation removed from db successfully.";
            }
            else
            {
                citationResponse.Accepted = false;
                citationResponse.Message = "Citation NOT deleted.";
            }
        }
        catch (Exception ex)
        {
            citationResponse.Accepted = false;
            citationResponse.Message = ex.Message;
            citationResponse.RmsException = new Exception(ex.Message);
            throw;
        }

        return citationResponse;            
    }
}

My POST method is fully populated with the JSON object I pass to it, all the values are there and usable. The same EXACT object in my GET action has null values for all of the properties. Here's a sample payload:

{"ReportNumber":"TEST275455",
 "ReportNumber_Prefix":"2013",
 "AgencyId":"BBC",
 "Status":"COMP",
 "ViolationDateTime":"9-21-2013 12:00:00"}

I'm stumped, and pulling my hair out trying to figure out why the Get is different from the Post?? Your help is GREATLY appreciated!

Eddie
  • 1,088
  • 3
  • 13
  • 29

1 Answers1

2

You've added routing info to the CitationRequest Request DTO:

[Route("/citations/{ReportNumber}/{ReportNumber_Prefix}/{AgencyId}", "GET, DELETE, OPTIONS")]
[Route("/citations", "GET, POST, DELETE, OPTIONS")]
public class CitationRequest : IReturn<CitationResponse>

Suggesting it's available by Get, but you're implementation expects a Citation instead?

public CitationResponse Get(Citation citation)

The declaration for Post uses the correct one:

public CitationResponse Post(CitationRequest request)
mythz
  • 134,801
  • 25
  • 234
  • 373
  • Thanks mythz, I fixed that issue but my JSON object is still null when it enters the Get action. Also, my DELETE action is now returning a 405 method not allowed...?? When I look at the headers for the DELETE, the allowed verbs do not include DELETE, but I've put it all over the code, everywhere I put the other verbs, so how can it not include it in the response? The more I try to fix this, the worse it gets... – Eddie Sep 27 '13 at 20:16
  • @Eddie See this answer for the [405 DELETE issue](http://stackoverflow.com/a/10283694/85785). Request DTOs should never be `null` but based on your Request Headers it should be **empty** since you're not populating any of the Request DTO properties in the QueryString or in the matching PathInfo. – mythz Sep 27 '13 at 20:25
  • Thanks for the info on the 405 DELETE - I'll try the web.config entry. Not sure how to implement the IIS change, since there is no file extension for ServiceStack... Also, the DELETE was working for a couple weeks before I did something to mess it up today. Regarding the Request DTO, yes your're right, it's not null, but it is empty. But if I call the service at '/citations' and pass in this JSON object in the payload above, shouldn't it populate the CitationRequest with the values? That's how it works for the POST, and used to work with the DELETE - why not with GET? – Eddie Sep 27 '13 at 20:46
  • @Eddie, if you use jQuery Ajax call, I think in GET you should have a URL with the request parameters. Get does not take a json object. If I am wrong, mythz can correct me, please, use POST otherwise. – stefan2410 Sep 27 '13 at 20:49
  • What payload? `GET` can't have a payload, you need to use the url, either with the querystring or pathInfo. You can also pass complex objects on the QueryString with the JSVFormat, see [the wiki docs](https://github.com/ServiceStack/ServiceStack/wiki/Serialization-deserialization) – mythz Sep 27 '13 at 20:49
  • @stefan2410 yep as per the HTTP Spec, `GET`'s can't have a request body. – mythz Sep 27 '13 at 20:50
  • @Eddie, in C# and JsonServiceClient.Get or Delete, you can use a IReturn object and the client will construct the Uri, not in jQuery Ajax. – stefan2410 Sep 27 '13 at 20:56
  • Yep, that's the bit I forgot... I was doing it using url params and I had removed that. Once I put that back in the GET worked as before. Thanks! Now I just need to figure out how to get the DELETE working. The web.config change recommended didn't make a difference. I still get the 405... – Eddie Sep 27 '13 at 21:27
  • @Eddie I can't see your `Delete()` implementation, does it take a `CitationRequest` and the url used matches the above route? The `X-Powered-By` HTTP Response header will tell you if the Request made it to ServiceStack or not. If not it's likely a conflicting IIS module or filter. Another option is to [tunnel DELETE's over HTTP POST's](http://stackoverflow.com/a/12729435/85785). – mythz Sep 27 '13 at 21:44
  • I added the Delete implementation to the code above - I had left it out originally because it wasn't particularly related to the original issue... I thought... So yes, it takes a CitationRequest and I pass it in using a JSON payload - but I'm guessing Delete can't have one either? I could have sworn that's how I did it, but maybe not... Now I'm getting a 403 Forbidden error when I try to access the Delete, so I think I'm pretty well hosed at this point. I'll try again next week. Thanks for your help!! – Eddie Sep 27 '13 at 22:41
  • Nope `Delete`'s can't have one either, the behavior is undefined in the HTTP spec but many web servers and fx's treat them like `GET`'s and don't allow it (inc SS). Changing it to url/queryString should help. – mythz Sep 27 '13 at 23:21
  • @mythz BTW, where Eddie found the httpRes.EndRequestWithNoContent() ? I just downloaded the v3 snapshot ver 3.9.60 (nuget has problem with ServicStack.Text 3.9.59). I cannot find this extension method nor the old I use httpRes.EndServiceStackRequest(). Also the v3 snapshot is the last in 3 version ? or you will update it again ? thank you in advance. – stefan2410 Sep 27 '13 at 23:45
  • ok, I found where extensions live now, adding using ServiceStack; :-) – stefan2410 Sep 27 '13 at 23:50