Search code examples
c#dotnet-httpclient

Rewriting a Java HttpURLConnection method using C# HttpClient


I've got a working Java method that uses java.net.HttpURLConnection that I should re-implement in C# using the .NET HttpClient.

Java method:

public static String getMyThingAPIToken() throws IOException{
    URL apiURL = new URL("https://myThingAPI/token");
    HttpURLConnection apiConnection = (HttpURLConnection) apiURL.openConnection();
    apiConnection.setRequestMethod("POST");
    apiConnection.setDoOutput(true);
    String apiBodyString = "myThingAPI login id and secret key";
    byte[] apiBody = apiBodyString.getBytes(StandardCharsets.UTF_8);

    OutputStream apiBodyStream = apiConnection.getOutputStream();
    apiBodyStream.write(apiBody);
    
    StringBuffer apiResponseBuffer;
    try (BufferedReader in = new BufferedReader(new InputStreamReader(apiConnection.getInputStream()))){
        String inputline;
        apiResponseBuffer = new StringBuffer();
        while((inputline = in.readLine()) != null) {
            apiResponseBuffer.append(inputline);
        }
    }
}

So far, my C# looks like below, and you'll notice that this early form of my implementation does not interpret the response. Nor does it have a string return type required for the token string.

This is because when I test it, the response has: StatusCode: 400 ReasonPhrase: 'Bad Request'

So something in my apiBody byte array or use of PostAsync must be different to what the Java method does, but I cannot work out what it could be.

public async static Task<HttpResponseMessage> getMyThingAPIToken(HttpClient client)
{
    var apiURI = new Uri("https://myThingAPI/token");
    string apiBodystring = "myThingAPI login id and secret key";
    byte[] apiBody = System.Text.Encoding.UTF8.GetBytes(apiBodystring);
    var response = await client.PostAsync(apiURI, new ByteArrayContent(apiBody));
    return response;
}

Solution

  • The Java code doesn't specify a type which means that by default the request uses application/x-www-form-urlencoded. This is used for FORM POST requests.

    The default content type for ByteArrayContent on the other hand is application/octet-stream while for StringContent it's text/plain. FORM content is used through the FormUrlEncoodedContent class which can accept any Dictionary<string,string> as payload.

    The input in the question is not in a x-www-form-urlencoded form so either it's not the real content or the API is misusing content types.

    Assuming the API accepts proper x-www-form-urlencoded content, the following should work:

    var data=new Dictionary<string,string>{
        ["login"]=....,
        ["secret"]=.....,
        ["someOtherField"]=....
    };
    var content= new FormUrlEncodedContent(data);
    var response=await client.PostAsync(apiURI,content);
    

    To send any text using application/x-www-form-urlencoded, we need to specify the content type in StringContent's constructor:

    var contentType="application/x-www-form-urlencoded";
    var content= new StringContent(apiBodyString, Encoding.UTF8,contentType);
    var response=await client.PostAsync(apiURI,content);