0

I am trying to get one API (reports API) to talk to another API (stored procedure runner API) which is a solution we are sort of forced to adopt given that we created the first API in .NET Core 2.2 and the Sap.Data.SQLAnywhere.v4.5 drivers only play nice with the .NET 4.7.2 framework. So we segregated them to compensate. All the answers I have seen over the last 4 or 5 hours leads me to believe I am doing this correctly but it still doesn't work.

I can hit the stored procedure runner from Postman with a json/text body just fine and get results from the database. However, when I try to hit this from C# I was first getting Unsupported Media Type which I think I fixed but now I get 500 errors and when debugging through to the stored procedure runner from the reportsAPI I notice that I don't get a parameter passed from the body.

[HttpPost]
[Route("api/Reports/GetStuff")]
[ResponseType(typeof(ResponseObject))]
public ResponseObject GetStuff([FromBody]string report)
{
        var response = new ResponseObject();

        try
        {
            response = new ReportService().RunStuff(report);
        }
        catch (Exception e)
        {
            throw;
        }

        return response;
}

The above is the SprocRunnerAPI and I get as far as

response = new ReportService().RunStuff(report);

before it fails out because it has nothing in "report".

public class ApiService
{
    public static HttpClient ApiClient { get; set; }

    public static void InitializeClient()
    {
        ApiClient = new HttpClient();
        //ApiClient.BaseAddress = new Uri("");
        ApiClient.DefaultRequestHeaders.Accept.Clear();
        ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }
}

This is where I initialize everything then use it in the following class method:

public ResponseObject RunReportToCSV(string report)
    {
        var rep = new ResponseObject();
        ApiClient.ApiService.InitializeClient();
        string url = @"http://localhost/EnterpriseReportRunner/api/reports/GetStuff";

        var httpContent = new StringContent(report, Encoding.UTF8, "application/json");

        //ServicePointManager.Expect100Continue = false; I honestly don't know where this goes... or if it is needed.

        var response = ApiClient.ApiService.ApiClient.PostAsync(url , httpContent).Result;

        if (response.IsSuccessStatusCode)
        {
            rep = response.Content.ReadAsAsync<ResponseObject>().Result;
        }
        else
        {
            throw new Exception(response.ReasonPhrase);
        }

        return rep;
}

I get as far as

var response = ApiClient.ApiService.ApiClient.PostAsync(url, httpContent).Result;

when it calls the aforementioned api method externally and it fails out. I had noticed no differences between the bodies in what I put in C# and what I put in Postman nor did I notice a difference looking through Fiddler on the Headers when making either call. I am seriously confused as to why what appears to be what I have seen everywhere used, that this is not working.

Thank you.

Adding Stack Trace from the SprocRunnerAPI… this isn't much help because I know exactly why this happens. By this point I expect a JsonSerialized object and I don't have it because it wasn't passed from the first API to the second.

at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at EnterpriseReportRunner.Service.ReportService.RunStuff(String report) in C:\Workspace\Solution.NET\LOB\InternalReportsAdminConsole Solution\EnterpriseReportRunner\EnterpriseReportRunner\Service\ReportService.cs:line 27 at EnterpriseReportRunner.Controllers.ReportsController.GetStuff(String report) in C:\Workspace\Solution.NET\LOB\InternalReportsAdminConsole Solution\EnterpriseReportRunner\EnterpriseReportRunner\Controllers\ReportsController.cs:line 67

To be clear this is the stack from the SprocRunnerAPI not the ReportsAPI that calls it. I am trying to find the body inside the HttpContext to see the difference between the Postman call and the C# call but can't seem to find it, is it really buried in the InputStream?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Slagmoth
  • 173
  • 1
  • 10
  • I had a similar issue when my IIS was not supporting the file MIME Type type/ streaming format. Do you have access to the IIS that's hosting your system? – Walter Verhoeven Aug 14 '19 at 18:29
  • You definitely shouldn't use `.Result` on an unawaited task. You could use `async/await` (or use `ContinueWith()` if you didn't want to make your method async), something like `rep = await response.Content.ReadAsAsync();` wrapped in a `try/catch` which will get any bubbled up errors. – bcr Aug 14 '19 at 18:30
  • @bcr Yeah, I forgot to say to ignore the obvious issues like that... I just need it to work first then I can clean it up. I realize there is insufficient error/handling in the sample code I will clean it up when it *runs*. Thanks. – Slagmoth Aug 14 '19 at 18:39
  • @ComputerAidedTradingSystems Yes, I just have it local at the moment and it was set to all defaults... – Slagmoth Aug 14 '19 at 18:42
  • Do you know where it's 500-ing? Do you see a stack trace? One place to look is the Windows Event Viewer. – bcr Aug 14 '19 at 18:43
  • guessing you're having an exception do you log the responses on the server, perhaps post the trace? – Walter Verhoeven Aug 14 '19 at 18:45
  • Actually... my co-worker found this [post](https://stackoverflow.com/questions/50458507/c-sharp-web-api-sending-body-data-in-http-post-rest-client)... seems redundant to have to serialize an already serialized jsonobject but that is what did the trick. Maybe one of the mods can mark this as duplicate? – Slagmoth Aug 14 '19 at 19:21

1 Answers1

0

The issue seems to be one that I thought would be redundant. The bottom line is that even if you expect a serialized JSON object you need to serialize it again, otherwise when you pass the StringContent via the HttpClient the body contents don't show in the receiving controller's parameter for consumption.

var httpContent = new StringContent(JsonConvert.SerializeObject(report), Encoding.UTF8, "application/json");

Even though "report" is already a string... really weird to me. But that solves it.

Found out that the strings that come into a controller are also wrapped inside another object so .Root seems to be your friend when deserializing. At least I learned something from the experience.

Thank you for all your input. Might want to link this to this other question.

Slagmoth
  • 173
  • 1
  • 10