Extension for send a jSon to a webService

Hi everyone,

I’m creating a extension for a cloud installation to send a jSon to a external webService. The code is like this:

 procedure SendContacts(var pContactTxt: text)
    var
        lMensaje: Text;
    begin
        Content.Clear();
        Content.GetHeaders(Headers);
        lMensaje := pContactTxt;
        Content.WriteFrom(lMensaje);
        Headers.Remove('Content-Type');
        Headers.Remove('Charset');
        Headers.Add('Content-Type', 'application/json');
        Headers.Add('Charset', 'utf-8');
        httpRequest.Content(Content);
        httpRequest.Method('POST');

        url := 'http://xxxxxx.com/webservice/create-contact';
        client.Clear();
        Client.SetBaseAddress(url);

        if not Client.Send(httpRequest, ResponseMessage) then
            error('Llamada incorrecta');
        if not ResponseMessage.IsSuccessStatusCode then
            Error('El WS ha devuelto el siguiente error:\' +
            'Status Code : %1\' +
            'Descripcion: %2',
            ResponseMessage.HttpStatusCode,
            ResponseMessage.ReasonPhrase);

        ResponseMessage.Content.ReadAs(ResponseText);
        Message('Contacto(s) enviados a Gremious');
    end;

If I use “RequestBin” for testing, you can see that the message seems to be correct:

And if I copy the request content, adn send it to the real WebServiche through Postman, we can see in the WebService’s log that the message arrives correctly:

pastedimage1570516882446v2.png

But if I send directly to the webService from BC, I get this error:

pastedimage1570516986877v3.png

And this can be seen into the webService log:

Seems that the jSOn is sent as an array, and the webService isn’t capable to process it. Am I sending he content incorrectly?

Thank you all

Which content type are you setting when sending the message from Postman?

I suppose that it should be text/plain, not application/json.

I’m sending with “form-data”

I’ve changed in the extension to:

Content.Clear();
Content.GetHeaders(Headers);
lMensaje := pContactTxt;
Content.WriteFrom(lMensaje);
Headers.Remove('Content-Type');
Headers.Remove('Charset');
Headers.Add('Content-Type', 'multipart/form-data');
Headers.Add('Charset', 'utf-8');
httpRequest.Content(Content);
httpRequest.Method('POST');

And the log form the webService:

In this case not even an array arrives…

And did you try to send it as plain text? What is the outcome in this case?

HI,

In this way:

        Content.Clear();
        Content.GetHeaders(Headers);
        lMensaje := pContactTxt;
        Content.WriteFrom(lMensaje);
        Headers.Remove('Content-Type');
        Headers.Remove('Charset');
        Headers.Add('Content-Type', 'text/plain');
        Headers.Add('Charset', 'utf-8');
        httpRequest.Content(Content);
        httpRequest.Method('POST');

I get this error:

Basically the same…

I experimented today a little with JSon arrays sent from BC - first using Azure functions in C# and JavaScript, but then I found out that I have Apache with PHP configured on my laptop, and tried the same with json_decode.

<?php
	$requestBody = file_get_contents('php://input');
	$req = json_decode($requestBody, true);

	$responseString = "Hello, ";
	foreach ($req["names"] as $key => $value)
		$responseString .= "{$value['name']} ";

	header('Content-Type: application/json');
	echo json_encode($responseString);
?>

A variation of the classics - “Hello World”, but with a list of names in the body of the POST request.

And the data I used is a slightly simplified version of your message: a json object containing an array of objects:

{ 
    "names": [ 
        { "name": "Azure" },
        { "name": "Cloud" }
    ]
}

And it works without any problems, no matter if I send content as application/json or text/plain. Looks like there is some conversion happening on the server side before the request body is passed to json_decode.

Hi [mention:6f49de73c19f42ef93502a6cf846790b:e9ed411860ed4f2ba0265705b8793d05],

Thanks for your answer,as always. Seemes that you’ve had the same issue as me, but I don’t understand what are you seggesting me to do…

You are talking about server side, is that NAV? I must say that this development is an extension created for BC in cloud version

That’s the matter - I did not have any issues. JSon string gets decoded into an array or object - that code snippet I posted returns the result string with the state code 200.

I’m guessing if it could be the Laravel middleware controller which interferes here?

So as far as I understand, the jSOn is created as an array, because the string is created with the delimiters and all that, am I right?

You return the result string with “echo json_encode($responseString);” instruction I deduce.

The web service I must send to the jSon file, as we can see needs to receive a text, but is there any way to do that with an AL edtension for BC on cloud?

From an installation in NAV 2018 I send a jSon file to a webService of the same provider, without any problem…

It seems that the json string is actually correct, and your experiment with Request Bin confirms it. Logged result rather looks as if the initial json string was already deserialized and your custom contact processor receives the contents of the json array instead of the string that is being sent. Probably deserialization is already done by the framework.

That very first error message you posted in the question - do you know if it’s thrown by the Illuminate routing controller, or by ContactController?

Are you talking about the 500 error?

I don’t know wo is throwing that error, the web service we are attacking is not ours…

Anyway, the error log gives in this case :

So, if my jSOn is correctly created, the solutions should be to change something in the webService? It’s quite anoying to be able to solve this in earlier versions, but find this type of problems in the last version…

This information should be in the callstack, just below the cut in the screenshot. Is Illuminate\http\Request raising the error, or something else?

Request string is formed correctly, but Laravel and Symfony http client on which it is built, rely on request headers to figure out payload type. If it worked in a previous version of NAV, but started failing in BC, first of all I would carefully compare all http headers sent in both cases - in NAV and BC. Do they match?

Well, I must say that the service I’m using in NAV 18 and this oen are different, but the system provider is the same company.

In NAV18 I’m connecting like that:

HttpWebRequest := HttpWebRequest.Create('http://XXXXXX.com/ws/api/rest');
 HttpWebRequest.Method := 'POST';
 HttpWebRequest.ContentType('application/x-www-form-urlencoded');
 postString := STRSUBSTNO('json=%1',pJson);
 StreamWriter := StreamWriter.StreamWriter(HttpWebRequest.GetRequestStream);
  StreamWriter.Write(postString);
  StreamWriter.Close;
  StreamWriter.Dispose;
  HttpWebRequest.GetResponse;

If I use form-urlencode contetn type, I get this error in the actual webService:

Sorry but when you talk about laravel, callstack… I feel I’m quite lost…

Hold on! That’s a totally different message, with the message payload encoded in a different way - it’s not actually json if you are sending it as a url-encoded string. So, what we are talking about here is not a problem of BC sending incorrect data in the request - it’s rather about agreeing on the message format between BC and the server.

I mentioned Laravel above, because I see from the error log that the server receiving messages runs a php application based on this framework. One thing that Laravel does - it reads the content-type header, and if it contains the line ‘/json’, the message body is decoded by json_decode. Otherwise, content is passed on as is, without decoding.

Message is sent by the controller dispatcher to a particular implementation of the controller, which is declared as an abstract class in the framework. This is your ContactController, which is supposed to create contacts from the message content.

Laravel and its HTTP base layer Symfony are common open-source frameworks, but I have no idea what is happening inside the custom controller extending the framework

The solution to this problem should be to clarify the exact message format that the server is supposed to receive, or at least, compare the message (including all the headers) sent from NAV 2018 and mimic it in the new extension - Request Bin could help here.

Hi Alexander,

Thank you as always. We’ve done different tests, and finally the service provider made a small change into the webService, and the comunication is working.

Now I have another problem when that service calls to my published WebService, but hat’s another stuff, probably for another different post…

THnak you again, really appreciate your time and tips!!!