Warm tip: This article is reproduced from stackoverflow.com, please click
api asp.net c# httpclient token

httpclient Displays a negative answer

发布于 2020-04-23 13:51:13

First , a function that creates the token. Then, calls GetClient(real token)

public static HttpClient GetClient(string token)
{
  HttpClient client = new HttpClient();
  HttpContent content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("ContentType", "application/json"), new KeyValuePair<string, string>("Authorization", "Bearer '" + token + "'") });
  var response = client.PostAsync(new Uri("https://api.sandbox.paypal.com/v2/checkout/orders"), content).Result;
  if (response.IsSuccessStatusCode)
  {
    var responseContent = response.Content;
    string responseString = responseContent.ReadAsStringAsync().Result;
  }
  return client;
}

The error is:

{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{Paypal-Debug-Id: 5341e7ff8a884
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Date: Mon, 10 Feb 2020 05:36:37 GMT
Content-Length: 244
Content-Type: application/json
}}

What is the fix for such a thing??

Questioner
user12724182
Viewed
62
Ignas Z. 2020-02-11 04:18

There are two main parts of HTTP request that you have to understand to make this work:

  • Headers
  • Content

Headers is the first part of HTTP request which contains information about request like authorization, content length, content format, etc.

Second part is content. This is your actual data that you want to pass to the server. Content can be formatted in many different ways and your headers are supposed to inform server which type of formatting is used. Two of those formats that are referenced in your snippet are:

  • Form Url/Encoded - this data type is typicaly used with HTML forms. When you have something like

<form>
    <input name="key1" value="value1"/>
    <input name="key2" value="value2"/>
</form>

this form data is encoded as key1=value1&key2=value2.

  • Json - this is what you want to use for calling PayPal API, it's basically just Json structure appended as content with appropiate headers informing server that it should parse content as Json.

You are having problems because you are conflating headers with content and Form/UrlEncoded and Json content types. FormUrlEncodedContent constructor expects list of form key/value pairs, not headers and when you pass content-type and authorization to it, those key/value pairs are treated as data, not headers. This is where 401 error comes from as server is looking for authorization header not key/value pair in content. Try something like this:

public static HttpClient GetClient(string token)
{
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var yourContent = new
    {
        key1 = "value1",
        key2 = "value2"
    };
    var jsonContent = JsonConvert.SerializeObject(yourContent);
    var content = new StringContent(jsonContent, Encoding.ASCII, "application/json");

    var response = client.PostAsync(new Uri("https://api.sandbox.paypal.com/v2/checkout/orders"), content).Result;
    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content;
        string responseString = responseContent.ReadAsStringAsync().Result;
    }
    return client;
}

First of all, I moved authentication from content to headers. You just need to create a new AuthenticationHeaderValue and pass "Bearer" as scheme (I haven't looked it up, but from your code I assume that API used Bearer authentication scheme).

Next, it seems that you want to use Json content-type. So you have to genereate Json as content. I perform that using anonymous data type and passing it to JsonConvert (you'll need Newtonsoft.Json package, if you copy this code Visual Studio should automaticaly suggest installing that package for you).

Finally, to add Json content to request you should use StringContent and pass both your generated Json string and content-type "application/json".

EDIT: Tested your code, I assume your latest error was no longer 401, but rather 400 - Bad Request, caused by invalid content structure. Or it might have been 401 caused by invalid token parsing (maybe response length differs a bit?). Either way, updated code to correctly convert objects back and forth to JSON.

public class TokenResponse
{
    [JsonProperty(PropertyName = "scope")]
    public string Scope { get; set; }

    [JsonProperty(PropertyName = "access_token")]
    public string AccessToken { get; set; }

    [JsonProperty(PropertyName = "token_type")]
    public string TokenType { get; set; }

    [JsonProperty(PropertyName = "app_id")]
    public string AppId { get; set; }

    [JsonProperty(PropertyName = "expires_in")]
    public int ExpiresIn { get; set; }

    [JsonProperty(PropertyName = "nonce")]
    public string Nonce { get; set; }
}

public class Amount
{
    [JsonProperty(PropertyName = "currency_code")]
    public string CurrencyCode { get; set; }

    [JsonProperty(PropertyName = "value")]
    public string Value { get; set; }
}

public class PurchaseUnit
{
    [JsonProperty(PropertyName = "amount")]
    public Amount Amount { get; set; }
}

public class OrdersRequest
{
    [JsonProperty(PropertyName = "intent")]
    public string Intent { get; set; }

    [JsonProperty(PropertyName = "purchase_units")]
    public PurchaseUnit[] PurchaseUnits { get; set; }
}

public static void CreateToken()
{
    var client = new HttpClient();
    byte[] authBytes = Encoding.ASCII.GetBytes("user:pass");
    string base64Auth = Convert.ToBase64String(authBytes);
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64Auth);

    var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("grant_type", "client_credentials") });
    var response = client.PostAsync(new Uri("https://api.sandbox.paypal.com/v1/oauth2/token"), content).Result;
    if (response.IsSuccessStatusCode)
    {
        var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(response.Content.ReadAsStringAsync().Result);
        GetClient(tokenResponse.AccessToken);
    }
}

public static HttpClient GetClient(string token)
{
    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var request = new OrdersRequest
    {
        Intent = "CAPTURE",
        PurchaseUnits = new PurchaseUnit[] { new PurchaseUnit
            {
                Amount = new Amount
                {
                    CurrencyCode = "USD",
                    Value = "100.0"
                }
            }
        }
    };

    var jsonContent = JsonConvert.SerializeObject(request);
    var content = new StringContent(jsonContent, Encoding.ASCII, "application/json");

    var response = client.PostAsync(new Uri("https://api.sandbox.paypal.com/v2/checkout/orders"), content).Result;
    if (response.IsSuccessStatusCode)
    {
        var responseString = response.Content.ReadAsStringAsync().Result;
    }
    return client;
}