8

I am trying to find a way how to transfer curl command to C#.

What i need is to obtain token from api and save it into file C:\...\x.json\

Then i want to declare token into variable, and use it into another curl POST request to take data to work with them.

Curl:

curl -X POST "https://example.com" 
-H "accept: application/json" 
-H "Content-Type: application/json" 
-d {"username":"username","password":"password"}

My current try:

static void Main(string[] args)
{
    using (var httpClient = new HttpClient())
    {
        using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://example.com"))
        {
            request.Headers.TryAddWithoutValidation("Accept", "application/json");

            request.Content = new StringContent("{\"username\":\"username\",\"password\":\"password\"}", Encoding.UTF8, "application/json");

            var response = await httpClient.SendAsync(request);
        }
    }                        
}

I tried several things, but none of them was working. This one is from curl.olsh.me but also i am getting some await error what i don't know what to do with it. (I am newbie in C#) :

The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.

curl -v output:

    • Trying 10.0.0.0...
    • TCP_NODELAY set
    • Connected to example.com (10.0.0.0) port 443 (#0)
    • ALPN, offering h2
    • ALPN, offering http/1.1
    • successfully set certificate verify locations:
    • CAfile: C:\Users\lvrabel\Downloads\curl-7.60.0-win64-mingw\curl-7.60.0-win64-mingw\bin\curl-ca-bundle.crt CApath: none
    • TLSv1.2 (OUT), TLS handshake, Client hello (1):
    • TLSv1.2 (IN), TLS handshake, Server hello (2):
    • TLSv1.2 (IN), TLS handshake, Certificate (11):
    • TLSv1.2 (IN), TLS handshake, Server key exchange (12):
    • TLSv1.2 (IN), TLS handshake, Server finished (14):
    • TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
    • TLSv1.2 (OUT), TLS change cipher, Client hello (1):
    • TLSv1.2 (OUT), TLS handshake, Finished (20):
    • TLSv1.2 (IN), TLS handshake, Finished (20):
    • SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
    • ALPN, server did not agree to a protocol
    • Server certificate:
    • subject: OU=Domain Control Validated; OU=PositiveSSL Wildcard; CN=*.example.com
    • start date: Dec 1 00:00:00 2016 GMT
    • expire date: Dec 1 23:59:59 2019 GMT
    • subjectAltName: host "example.com" matched cert's "*.example.com"
    • issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Domain Validation Secure Server
    • SSL certificate verify ok. < POST /cxf/IOTServices/v2/Login HTTP/1.1 < Host: example.com < User-Agent: curl/7.60.0 < accept: application/json < Content-Type: application/json < Content-Length: 64 <
    • upload completely sent off: 64 out of 64 bytes < HTTP/1.1 200 OK < Date: Mon, 24 Sep 2018 09:22:30 GMT < Content-Type: application/json < Date: Mon, 24 Sep 2018 09:22:31 GMT < Connection: close < Set-Cookie: TS0119a36f=015d568915873c94e852616fd92d5dd7a03a620f3cd2326781154684dfd7b31302f6fcb9822598d5a76466d0c6a25583fccbb30c7d; Path=/; Domain=.example.com < Transfer-Encoding: chunked < {"code":200,"status":"success","sessionId":"5cd92d80-bfdb-11e8-83c7-5b742f0c1244"}* Closing connection 0
    • TLSv1.2 (OUT), TLS alert, Client hello (1):
LukasV
  • 271
  • 1
  • 3
  • 12
  • Your method sig should look like: `static async void Main(string[] args)`. You add the `async` keyword. – Stuart Sep 21 '18 at 11:55
  • Hi @Stuart, then i don't understand it, because when i change Main method then i got error : Program does not contain static Main method suitable for an entry point – LukasV Sep 21 '18 at 11:59
  • You might want to define a task method, and then call that from within Main... – Stuart Sep 21 '18 at 12:02
  • @LukášVrábel check my answer, that might solve your issue. – Maytham Fahmi Sep 03 '19 at 11:32

6 Answers6

5

use this website https://curl.olsh.me/

using (var httpClient = new HttpClient())
{
    using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://example.com/"))
    {
        request.Headers.TryAddWithoutValidation("Accept", "application/json"); 

        request.Content = new StringContent("{\"username\":\"username\",\"password\":\"password\"}", Encoding.UTF8, "application/json"); 

        var response = await httpClient.SendAsync(request);
    }
}
Jin Thakur
  • 2,711
  • 18
  • 15
5

As far as I know you need to convert username and password to bytes, and take it as base 64 string and pass it to header. When you use await, if it is a void method then you should add async modifier with Task in its signature OR in case the method should return what ever type than use async Task as shown in the example. In that case you need to fetch the result, using result as shown in the test example. You can read more on this topic in Microsoft docs.

Here is the code:

public async Task<HttpResponseMessage> CreateAuthorization(string url, string username, string password)
{
    HttpResponseMessage response;

    using (var httpClient = new HttpClient())
    {
        using (var request = new HttpRequestMessage(new HttpMethod("POST"), url))
        {
            request.Headers.TryAddWithoutValidation("Accept", "application/json");
            var authorization = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}"));
            request.Headers.TryAddWithoutValidation("Authorization", $"Basic {authorization}");
            response = await httpClient.SendAsync(request);
        }
    }

    return response;
}

Here is example of testing it:

public void CreateAuthorizationTest()
{
    var response = CreateAuthorization("https://example.com", "username", "password");
    var result = response.Result;
    var o = result.Content.ReadAsStringAsync();

    if (result.IsSuccessStatusCode)
    {
        //do some thing if success
        // you can do deserialization what ever
        Console.WriteLine(o);
    }
    else
    {
        //do some thing if error
        Console.WriteLine(o);
    }
    //etc.
}

Links:

Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
2

Async main methods is available in C# 7. If you're on an earlier version, you can handle the task 'manually' like this

var task = httpClient.SendAsync(request);
task.Wait();
var response = task.Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Hans Kilian
  • 18,948
  • 1
  • 26
  • 35
  • Nice that is working, thank you for that. I do have respond from server right now, but sadly i see just this : StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: Where should be also token, but i dont see it. – LukasV Sep 21 '18 at 12:38
  • Actually more specifically, since 7.1 – Mohammed Noureldin Sep 21 '18 at 12:40
  • @LukášVrábel Since you're getting a 200 return code, the call is going through to the server. You could try to debug on the server side if you have access to it to see what is going on. If you don't have access to the server, you could try getting the call to work with Postman or something like that. – Hans Kilian Sep 21 '18 at 12:46
  • Sadly I don't have access to that server. Syntax of the token is something like this: _6fa3ea90-bc02-11e8-991a-d35512019464_. That should be problem for Encoding right? – LukasV Sep 21 '18 at 12:49
  • Can you post the output from curl in your question? – Hans Kilian Sep 21 '18 at 12:54
  • @HansKilian Sorry i was out of PC at the weekend. Here is the output i need: _{"code":200,"status":"success","sessionId":"1306d850-ba68-11e8-991a-d35512019464"}_ – LukasV Sep 24 '18 at 06:23
  • My mistake. I wanted to see the headers, so could you run curl with the -v option and add the output to the question? – Hans Kilian Sep 24 '18 at 09:10
  • pastebin is blocked in our firewall :( – Hans Kilian Sep 24 '18 at 09:36
  • @HansKilian I added it to my original question. – LukasV Sep 24 '18 at 10:30
  • The only weird thing I can see is that you have 'accept' in your curl command but 'Accept' in the program. But 'Accept' should be the correct one. – Hans Kilian Sep 24 '18 at 11:02
  • I think that this one doesn't really matter. Whole ouput in console looks like this: StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Connection: close Pragma: no-cache Cache-Control: no-cache Content-Length: 738 Content-Type: text/html; charset=utf-8 } – LukasV Sep 24 '18 at 11:23
  • The content you get back is an HTML page. That's usually an error page when that happens. Can you check if there's any useful information in it? – Hans Kilian Sep 24 '18 at 11:43
  • I have no idea how to check it in html sorry. Can you tell me how please? – LukasV Sep 24 '18 at 11:56
  • If i add it into the code i get request declined. But when I leave it as it was so just Console.WriteLine(response); then the statuscode is 200 so i have no idea... – LukasV Sep 24 '18 at 12:15
2

You have to make the main method async:

static async void Main(string[] args)

If you are using C# version < 7.1, then you cannot use any await directly in Main. In this case you have to define a Task and .Wait() for it (instead of await for it).

Task task = ....;
task.Wait();
var result = task.Result;
Mohammed Noureldin
  • 14,913
  • 17
  • 70
  • 99
1

I use the System.Net.Http.Formatting.Extension

example:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
// Add NuGet package System.Net.Http.Formatting.Extension
namespace SimpleRestCall
{
    class Program
    {
        static CancellationTokenSource cts = new CancellationTokenSource();

        static readonly string sitePath = "https://example.com";
        static void Main(string[] args)
        {
            using (var client = new HttpClient { BaseAddress = new Uri(sitePath), Timeout = new TimeSpan(0, 1, 0) })
            {
                Task.Run(() => Login(client)).Wait();
            }
        }

        private static async Task Login(HttpClient client)
        {
            string path = ""; // if it's a controller path put it here
            LoginData postData = new LoginData { username = "username", password = "password" };
            string json;
            using (HttpResponseMessage resp = await client.PostAsJsonAsync(path, postData, cts.Token))
            {
                json = await resp.Content.ReadAsStringAsync();
            }
            Console.WriteLine(json);
            Console.WriteLine("Press a key to finish");
            string aa = Console.ReadLine();
        }

        private class LoginData
        {
            public string username { get; set; }
            public string password { get; set; }
        }
    }
}
Ian
  • 55
  • 4
1

Update:

I got this response from server :

StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Date: Tue, 25 Sep 2018 06:57:16 GMT Date: Tue, 25 Sep 2018 06:57:17 GMT Connection: close Set-Cookie: TS0119a36f=015d56898097bc7f5dccb374f352898f8806da5833c9f8a8ef4ff6ae2a2b1d4e78147fb79479f8bf85929406f34372c41c63572a89; Path=/; Domain=.example.com Transfer-Encoding: chunked Content-Type: application/json }

I think that there is some problem with encoding or idk. When i execute curl i get this output (the one i need):

{"code":200,"status":"success","sessionId":"350bf800-bfdb-11e8-991a-d35510009464"}

Current code:

    static void Main(string[] args)
    {
        using (var httpClient = new HttpClient())
        {
            using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api.example.com"))
            {
                request.Headers.TryAddWithoutValidation("accept", "application/json");
                request.Content = new StringContent("{\"username\":\"user\",\"password\":\"pass\"}", Encoding.UTF8, "application/json");

                var task = httpClient.SendAsync(request);
                task.Wait();
                var response = task.Result;

                Console.WriteLine(response);
                Console.ReadKey();
            }
        }
    }
LukasV
  • 271
  • 1
  • 3
  • 12