-3

I am pretty new in .NET and C# (I came from Java and Spring framework) and I am finding the following difficulties calling an API in the correct way.

I will try to explain my problem in details.

I have this API (defined into a project deployed into IIS. Note, this project contains also other APIs that I am calling without problem):

[HttpPost]
[Route("api/XXX/InviaAlProtocollo/{siglaIDUor}")]
public string InviaAlProtocollo(MailBuffer mailBuffer, string siglaIDUor)
{
    ..........................................................................
    DO SOMETHING
    ..........................................................................
}

As you can see it take 2 input parameters:

  • MailBuffer mailBuffer that should be into the request body.
  • siglaIDUor that is into the URI.

I have some problem trying to pass the first parameter.

NOTE: I can't change the code of this API because was made by someone else and it can have impact on other thing.

Into another project deployed elsewhere I am trying to call the previous API (from a controller method) in this way:

[SharePointContextWebAPIFilter]
[HttpGet]
[ActionName("InviaMailAlProtocollo")]
public IHttpActionResult InviaMailAlProtocollo(string siglaIdUor)
{

    Console.WriteLine("INTO InviaAlProtocollo()" + siglaIdUor);

    // Ignore self signed certificate of the called API:
    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

    // Create the byte array[]
    UTF8Encoding encoding = new UTF8Encoding();
    byte[] mailContent = encoding.GetBytes("TEST");

    // Create the MailBuffer object that have to be passed to the API into the request body:
    MailBuffer content = new MailBuffer();
    content.Nome = "blablabla";
    content.Buffer = mailContent;

    string jsonRequest = urlBaseProtocolloApi + "/api/XXX/InviaAlProtocollo/ABC123";

    // Setting my credentials:
    credCache.Add(new Uri(jsonRequest), "NTLM", myCreds);

    HttpWebRequest spRequest = (HttpWebRequest)HttpWebRequest.Create(jsonRequest);
    spRequest.Credentials = credCache;
    spRequest.UserAgent = "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0";
    spRequest.Method = "POST";
    spRequest.Accept = "application/json;odata=verbose";
    spRequest.ContentType = "application/json; charset=UTF-8";

    // Create and set the stream:
    spRequest.ContentLength = mailContent.Length;

    Stream newStream = spRequest.GetRequestStream();
    newStream.Write(mailContent, 0, mailContent.Length);

    newStream.Close();

    // Obtain the response from the API:
    HttpWebResponse endpointResponse = (HttpWebResponse)spRequest.GetResponse();

    string sResult;
    JArray jarray;

    // Parse the response:
    using (StreamReader sr = new StreamReader(endpointResponse.GetResponseStream()))
    {
        sResult = sr.ReadToEnd();

        jarray = JArray.Parse(sResult);
        //JObject jobj = JObject.Parse(sResult);
    }

    Console.WriteLine(jarray);

    return Ok(jarray);

}

The problem is that when this method call my API the received MailBuffer mailBuffer input parameter is null (I see it debuggin my API and calling it).

I suspect that the problem could be related to this code section of my call:

// Create and set the stream:
spRequest.ContentLength = mailContent.Length;

Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);

newStream.Close();

Probably I am trying to attach the wrong thing into the body of my request (the byte[] mailContent instead of the entire MailBuffer content object).

NOTE: To perform this call I have to use HttpWebRequest.

So, what is wrong? What am I missing? How can I fix this issue putting the entire MailBuffer content object into the body request and allowing my called API to retrieve it as input parameter?

AndreaNobili
  • 40,955
  • 107
  • 324
  • 596
  • 1
    How does this question differ from the other 3 questions you've already posted on the same problem? – A Friend Nov 23 '18 at 12:01
  • Why do you **have** to use `HttpWebRequest` to perform the call? – Nkosi Nov 27 '18 at 18:02
  • The Web API is not sending the data formatted to what the first API expects, so the model binder wont know how to populate the parameters. Check my submitted answer for more details. – Nkosi Nov 30 '18 at 03:39

3 Answers3

3

The other project should make sure that the request is made with properly formatted data expected by the other API.

Right now you are sending just the raw bytes of the test email in the body of the request

//...

// Create the byte array[]
UTF8Encoding encoding = new UTF8Encoding();
byte[] mailContent = encoding.GetBytes("TEST");

// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;

//...

Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length); //<---HERE ONLY SENDING encoding.GetBytes("TEST")

while the other endpoint is expecting data that can be deserialized to MailBuffer

Here is the portion of code that should be refactored to send the correct data

//...

UTF8Encoding encoding = new UTF8Encoding();
// Create the MailBuffer object that have to be passed to the API into the request body:
var content = new MailBuffer() {
    Nome = "blablabla",
    Buffer = encoding.GetBytes("TEST")
};
//convert model to JSON using Json.Net
var jsonPayload = JsonConvert.SerializeObject(content);
byte[] mailContent = encoding.GetBytes(jsonPayload); //<---CORRECT CONTENT NOW

spRequest.ContentLength = mailContent.Length;

Stream newStream = spRequest.GetRequestStream();
newStream.Write(mailContent, 0, mailContent.Length);

//...

Finally I would suggest using the simpler API of HttpClient to make the request. That however would be entirely up to your preference.

Here is an example of same call using HttpClient

[SharePointContextWebAPIFilter]
[HttpGet]
[ActionName("InviaMailAlProtocollo")]
public async Task<IHttpActionResult> InviaMailAlProtocollo(string siglaIdUor) {
    Console.WriteLine("INTO InviaAlProtocollo()" + siglaIdUor);
    // Ignore self signed certificate of the called API:
    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

    string requestUri = urlBaseProtocolloApi + "/api/XXX/InviaAlProtocollo/" + siglaIdUor;
    // Setting my credentials:
    credCache.Add(new Uri(requestUri), "NTLM", myCreds);

    var handler = new HttpClientHandler {
        UseDefaultCredentials = true,
        Credentials = credCache
    };

    var client = new HttpClient(handler);
    client.DefaultRequestHeaders.Add("UserAgent", "Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+NT+5.0");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json;odata=verbose"));

    // Create the MailBuffer object that have to be passed to the API into the request body:
    var buffer = new MailBuffer() {
        Nome = "blablabla",
        Buffer = Encoding.UTF8.GetBytes("TEST")
    };
    //convert model to JSON using Json.Net
    var jsonPayload = JsonConvert.SerializeObject(buffer);
    var mailContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
    // Obtain the response from the API:
    var response = await client.PostAsync(requestUri, mailContent);
    if (response.IsSuccessStatusCode) {
        var jarray = await response.Content.ReadAsAsync<JArray>();
        Console.WriteLine(jarray);
        return Ok(jArray);
    }
    return BadRequest();
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
2

Use [FromBody] parameter.

[HttpPost]
[Route("api/XXX/InviaAlProtocollo/{siglaIDUor}")]
public string InviaAlProtocollo([FromBody]MailBuffer mailBuffer, string siglaIDUor)
{
    ..........................................................................
    DO SOMETHING
    ..........................................................................
}

Also try passing MailBuffer as JSON object, it will be automaticly converted to MailBuffer object when you pass this from body.

If this won't work switch MailBuffer object in method with similar object and then map this object to MailBuffer.

  • 1
    `NOTE: I can't change the code of this API because was made by someone else and it can have impact on other thing.` – Nathan Werry Nov 29 '18 at 14:47
1

You can give a try with HttpClient (using System.Net.Http)

private static readonly HttpClient client = new HttpClient();

// Create the MailBuffer object that have to be passed to the API into the request body:
MailBuffer content = new MailBuffer();
content.Nome = "blablabla";
content.Buffer = mailContent;

var values = new Dictionary<string, object>
{
   { "mailBuffer", content },
   { "siglaIDUor", siglaIdUor }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("/api/XXX/InviaAlProtocollo/ABC123", content);

var responseString = await response.Content.ReadAsStringAsync();
DDan
  • 8,068
  • 5
  • 33
  • 52