microsoft / AL

Home of the Dynamics 365 Business Central AL Language extension for Visual Studio Code. Used to track issues regarding the latest version of the AL compiler and developer tools available in the Visual Studio Code Marketplace or as part of the AL Developer Preview builds for Dynamics 365 Business Central.
MIT License
737 stars 243 forks source link

AL HttpRequest /Httpclient "Get" Method with Key Content-Type #4097

Closed sankarvinop closed 5 years ago

sankarvinop commented 5 years ago

Bug Key "Content-Type" unable to add to HttpHeaders if we get http headers from HttpRequestMessage.Get(Headers).

Steps and/or AL code to reproduce the behavior:

  1. Go to '...' Create functions and local variables Headers : HttpHeaders; RequestMessage : HttpRequestMessage ;

Add code.

RequestMessage.GetHeaders(Headers); RequestHeaders.Add('Content-Type','application/x-www-form-urlencoded');

and try to call this function from BC. it will hit an error while adding key.

Expected behavior As per standard content-type, it must be added into HttpContent. but some of the API calls, we need to add this into part of header. because other system expect content-type key from header. if not exisit will simply rejects.

Screenshots If applicable, add screenshots to help explain your problem.

Versions: AL Verison: 2.0.28254

ftornero9 commented 5 years ago

That is a known issue, you first need to remove the "Content-Type", in your code like this:

RequestHeaders.Remove('Content-Type''); RequestHeaders.Add('Content-Type','application/x-www-form-urlencoded');

sankarvinop commented 5 years ago

I have already tried. still getting error message.

local procedure CheckHttpAPI() var Httpclient: HttpClient; httpRequest: HttpRequestMessage; httpResponse: HttpResponseMessage; HttpContent: HttpContent; HttpHeader: HttpHeaders; begin httpRequest.GetHeaders(HttpHeader); HttpHeader.Remove('Content-Type'); HttpHeader.Add('Content-Type', 'application/x-www-form-urlencoded');

end;

Microsoft Dynamics 365 Business Central

Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.

OK

javith2dj commented 5 years ago

Try

Httpcontent.Getheaders(Httpcontentheader); HttpcontentHeader.Add('Content-Type', 'application/x-www-form-urlencoded');

And set the content to httprequestmessage.

sankarvinop commented 5 years ago

I knew it will work if i added into content header. i also mentioned in my expected behavior. But i want to add this key in RequestHeader, because other system validates and accept if i add only in the request header.

larswestman commented 5 years ago

Isn't this the same: [https://github.com/Microsoft/AL/issues/3579]

StanislawStempin commented 5 years ago

Yes, it looks like the question has been answered in #3579

sankarvinop commented 5 years ago

I cannot use Getheader from content and assign keys. the reasons is ,I have to use GET method to call API.
Currently It wont support, if i add key 'Content-Type' in Content Header with GET method to call API.

I knew this is not a standard way of doing. but the API which i call expect this way to call API.

sankarvinop commented 5 years ago

This is the dot net code sample. to call API.

`var client = new RestClient("https://apisandbox.iras.gov.sg/iras/sb/Authentication/SingPassAuth?scope=REPLACE_THIS_VALUE&callback_url=REPLACE_THIS_VALUE&state=REPLACE_THIS_VALUE");

var request = new RestRequest(Method.GET); request.AddHeader("Accept", "application/json"); request.AddHeader("Content-Type", "application/json"); request.AddHeader("x-ibm-client-secret", "REPLACE_THIS_KEY"); request.AddHeader("x-ibm-client-id", "REPLACE_THIS_KEY"); IRestResponse response = client.Execute(request);`

i have to add both below items and rest request method should be "GET" request.AddHeader("Accept", "application/json"); request.AddHeader("Content-Type", "application/json");

due to validation in AL code, i am unable to this

StanislawStempin commented 5 years ago

Can you paste how the actual http request looks like? I'm not familiar with the RestClient or RestRequest classes from C# (I assume you're using RestSharp?).

The AL http stack is built on top of the standard C# HttpClient stack and the error message that you see comes directly from .NET. Are you sure that RestRequest.AddHeader is equivalent to HttpRequest.Header?

Perhaps try changing your C# code to the standard HttpClient first to see if it is supported. Then it will most likely work in AL as well.

sankarvinop commented 5 years ago

Please find code implemented in using httpclient. and able connect successfully. TryAddWithoutValidation is missing in httpheader.

`var header = httpClient.DefaultRequestHeaders; header.Add('accept',"application/json");//ACCEPT header header.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8"); header.Add("x-ibm-client-id", singpassConfig.XIBMClientId); header.Add("x-ibm-client-secret", singpassConfig.XIBMClientSecret); HttpResponseMessage response = await httpClient.GetAsync(singPassAuthUrl); if (response.IsSuccessStatusCode)

{

}`

sankarvinop commented 5 years ago

Hello Stanislaw Stempin,, Please let me know if you need any other information. Thanks

StanislawStempin commented 5 years ago

Thank you for the information, this is now clear to us and we will consider how to expose this functionality. The root cause seems to be that the service that you're calling expects an invalid http request (as determined by the .NET HttpClient stack).

JohanStenberg100 commented 5 years ago

@sankarvinop I'm trying your example with TryAddWithoutValidation and this does not send the header. Can you provide another C# example using HttpClient? Semantically your request doesn't make sense by the way, you can't modify the server or file a bug there instead?

sankarvinop commented 5 years ago
Logically a Get Request does not require a Content-Type header.

Singpass (Singapore National Authentication System) enforces a validation of content-type and accept header for all type of requests.

We have found adding the content-type header using the TryAddWithoutValidation methods of HttpClient in DOTNET ensure that the Singpass Server validation of the request passes and works.

We have implemented IRAS GST Return integration to Singpass using a DOTNET gateway which we want to port to BC, hence request this change.

We have already requested to Singpass for change, but it may be difficult to change at Singpass level since it has to go through many government departments.

Exposing the underling method TryAddWithoutValidation methods of HttpClient will allow us to integrate Singpass.
JohanStenberg100 commented 5 years ago

@sankarvinop please attach a ZIP file of a small console program which works with Singpass (you should of course emit authentication and the URL you are hitting), so I can understand fully what you are doing. Just a program doing a GET request with the Content-Type header.

SrivastavaDevesh commented 5 years ago

Hello Stanislaw ,

I am also getting the same issue. Do you have any solution for this? Please let us know if you have.

Thank you

JohanStenberg100 commented 5 years ago

Hi @SrivastavaDevesh as far as I understand, this is an issue with the server because it's not properly complying with the standard. I suggest you modify your server or request a modification instead. I will close this issue now.

bctails commented 5 years ago

I'm also getting this error (also in a PUT request):

Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.

Frankly, I don't really see the point of actually having validation on whether the header belongs in a Content-object or not. It might not comply with the standards, but why does Business Central try to enforce the rules while I can easily do this from PHP?

In a world full of webservices programmed by all kinds of business and people, we would benefit the most from not having any validation on our request at all; even though this request is "officially illegal", inferior design, the hairy pimple of programming - you name it - it is what the software I'm trying to connect to currently requires. I'm not in the position to ask anyone to make a change there (well.. I can ask, but trust me, it's not happening).

There's no pride to be kept here.

Just an extra example to prove how terrible these APIs can get: A little while ago, I had to connect to one using XML. In the end I parsed two XMLPorts through a text-variable to remove the XML start of the second one, similar to this:

`<?xml version="1.1"?>

blah ` The service on the other side wouldn't accept anything else, and they're not changing it since they are switching to JSON within the next year. Might hurt your eyes, but it works. So I'd like to request @JohanStenberg100 for a reopen and removal of unnecessary validation :-) edit: Actually, instead of doing a full hijack of this topic, I'll open a new one and refer back to this.