I am pretty new to C# and want to create a REST Get request with Basic auth.
I'm using RestSharp but i can't find a proper working full example.
This is my code:
using RestSharp;
using RestSharp.Authenticators;
namespace HttpClientAPP
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://data.myhost.de");
client.Authenticator = new HttpBasicAuthenticator("user", "xxxx");
var request = new RestRequest("resource",Method.Get);
client.ExecuteGetAsync(request).Wait();
Console.WriteLine("as");
}
}
}
This is thejson answer I expect and i want to parse it to use the data to store it in a MYSQL DB:
{"info": [
{
"method": "GET",
"url": "https://data.myhost.de/zfa-values/{zfaId}",
"description": "Get a ZFA value by id"
},
{
"method": "GET",
"url": "https://data.myhost.de/zfa-values",
"description": "Get a list of available ZFA values"
},
{
"method": "GET",
"url": "https://data.myhost.de/new-zfa-values",
"description": "Get a list of available ZFA values which have not been viewed yet"
},
{
"method": "GET",
"url": "https://data.myhost.de/",
"description": "Get a list of api endpoints"
}
]}
How do I parse the json response?
At https://restsharp.dev/v107/#recommended-usage there is some guidance (but at time of writing this answer I think it might have a typo)
Get docs says:
public class GitHubClient {
readonly RestClient _client;
public GitHubClient() {
_client = new RestClient("https://api.github.com/")
.AddDefaultHeader(KnownHeaders.Accept, "application/vnd.github.v3+json");
}
public Task<GitHubRepo[]> GetRepos()
=> _client.GetAsync<GitHubRepo[]>("users/aspnet/repos");
^^^^^^^^
//note: I think this should be GetJsonAsync
}
Post docs says:
var request = new MyRequest { Data = "foo" };
var response = await _client.PostAsync<MyRequest, MyResponse>(request, cancellationToken);
^^^^^^^^^ ^^^^^^^
//note: i think this should be PostJsonAsync and the first arg should be a string url path
You have classes that represent the json you send/get back, and RestSharp will de/serialize them for you. If you want some help making classes from JSON, take a look at services like http://app.QuickType.io - you paste your json and you get representative classes. The online generators are usually a bit more sophisticated in terms of what they interpret/the attributes they decorate with. Pasting your json into QT gives (you can choose a better name than SomeRoot):
namespace SomeNamespace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class SomeRoot
{
[JsonProperty("info")]
public Info[] Info { get; set; }
}
public partial class Info
{
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
}
public partial class SomeRoot
{
public static SomeRoot FromJson(string json) => JsonConvert.DeserializeObject<SomeRoot>(json, SomeNamespace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this SomeRoot self) => JsonConvert.SerializeObject(self, SomeNamespace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
And use like:
using RestSharp;
using RestSharp.Authenticators;
namespace HttpClientAPP
{
class Program
{
static async Task Main(string[] args)
{
var client = new RestClient("https://data.myhost.de");
client.Authenticator = new HttpBasicAuthenticator("user", "xxxx");
var res = await client.GetJsonAsync<SomeRoot>("");
Console.ReadLine(); //prevent exit
}
}
}
First, you can use newtosoft json nuget package:
Docs: https://www.newtonsoft.com/json
Example:
class ApiEndpoint{
public string Method {get;set;} // or you can use enum
public string Url {get;set;}
public string Description {get;set;}
}
// some logic
public void Fetch()
{
var client = new RestClient("https://data.myhost.de");
client.Authenticator = new HttpBasicAuthenticator("user", "xxxx");
var request = new RestRequest("resource",Method.Get);
var response = client.Get(request);
// read response as json
var json = JsonConvert.DeserializeObject<ICollection<ApiEndpoint>>(response);
// now json will have structure in your example
}
Other way is to add swagger gen for you server, get openapi.json and then generate sharp client using nswag
https://blog.logrocket.com/generate-typescript-csharp-clients-nswag-api/
Related
I want to optimize response returning from ASP.NET Core application. In order to do that I'm serializing the object to JSON UTF-8 bytes and inserting it to SQL as varbinary(max). I initially though that I'll get a stream from the column value using ADO.NET and just return it from Controller. This worked perfectly fine at the beginning as I've simply returned the Stream from the controller and it worked:
public async Task<Stream> GetSingleObject()
{
Response.ContentType = "application/json; charset=UTF-8";
var streamWithUtf8JsonBytesFromDb = await _repository.GetSingleObject();
return streamWithUtf8JsonBytesFromDb;
}
Unfortunately I have to attach additional properties to the response object:
{
"metadata": null,
"data": (here I would like to copy bytes from SQL stream directly to the response without any transformation)
"links": [
"self": "some url"
]
}
How can I use System.Text.Json to achieve this result in performant and usable fashion?
I've came up with two approaches so far, but I think there has to be a better way:
First solution:
//using Microsoft.AspNetCore.Http.Extensions;
//using Microsoft.AspNetCore.Http;
public async Task GetSingleObject()
{
Response.ContentType = "application/json; charset=UTF-8";
await Response.WriteAsync("{\"meta\":null,\"data\":", System.Text.Encoding.UTF8)
var streamWithUtf8JsonBytesFromDb = await _repository.GetSingleObject();
await streamWithUtf8JsonBytesFromDb.CopyToAsync(Response.Body)
await Response.WriteAsync($",\"links\":[{{\"self\":{Request.GetEncodedUrl()}}}]}}", System.Text.Encoding.UTF8);
}
Constructing JSON parts from string and concatenating it in the stream is obviously painful
Second solution:
public class Meta
{ /* Not relevant */ }
public class ObjectResponse
{
public Meta? Meta { get; set; }
public JsonDocument Data { get; set; }
public Dictionary<string, string> Links { get; set; }
}
//using Microsoft.AspNetCore.Http.Extensions;
//using Microsoft.AspNetCore.Http;
public async Task<ObjectResponse> GetSingleObject()
{
var streamWithUtf8JsonBytesFromDb = await _repository.GetSingleObject();
return new ObjectResponse {
Meta = null,
Data = await JsonDocument.ParseAsync(streamWithUtf8JsonBytesFromDb),
Links = new Dictionary<string, string> {
{ "self", Request.GetEncodedUrl()}
};
}
}
This is better than the first approach, but it adds unnecessary parsing of the stream which I'd like to omit.
What I'd think is the best idea is to use the following:
public class Meta
{ /* Not relevant */ }
public class ObjectResponse
{
public Meta? Meta { get; set; }
public Stream Data { get; set; }
public Dictionary<string, string> Links { get; set; }
}
//using Microsoft.AspNetCore.Http.Extensions;
//using Microsoft.AspNetCore.Http;
public async Task<ObjectResponse> GetSingleObject()
{
return new ObjectResponse {
Meta = null,
Data = await _repository.GetSingleObject(),
Links = new Dictionary<string, string> {
{ "self", Request.GetEncodedUrl()}
};
}
}
But it doesn't work, because System.Text.Json tries to serialize this stream instead of copying it directly to the response body (which makes sense of course). Am I missing something? Is there a custom converter I could use to achieve the result I'd like?
my deserialize Dictionary's key results in "brand[0]" when I send in "brand" to the api.
I have a class like this:
public class SearchRequest
{
public bool Html { get; set; } = false;
public Dictionary<string, HashSet<string>> Tags { get; set; }
}
// MVC Controller
[HttpPost]
public ActionResult Index(SearchRequest searchRequest)
{
...
}
And a json request like this that I post to the controller:
{
"html": true,
"tags": {
"brand": [
"bareminerals"
]
}
}
The binding seams to work and the searchRequest object is created but the resulting dictionary dose not have the key "brand" in it but insted the key "brand[0]" how can I preserve the real values I send in?
Edit: I need tags to be able to contain multiple tags, with multiple options, this was a simpel example.
One soulution to my problem is to create a custom model bind, so this is what am using now, but I dont understand why I need to, and I feel like there should be a easyer way? But am gonna leve It here anyhow.
public class FromJsonBodyAttribute : CustomModelBinderAttribute
{
public override IModelBinder GetBinder()
{
return new JsonModelBinder();
}
private class JsonModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stream = controllerContext.HttpContext.Request.InputStream;
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
var checkoutOrderDataStr = reader.ReadToEnd();
return JsonConvert.DeserializeObject(checkoutOrderDataStr, bindingContext.ModelType);
}
}
}
}
I'm not sure what is going on with your setup. You should not need a custom binder. I still think the problem is most likely with your calling code - whatever you're using as a client.
I'm using Asp.net Core 3.1. Here's what I threw together as a quick test.
Created Asp.net Core web application template with MVC. I declared two classes - a request POCO and a result POCO. The request was your class:
public class SearchRequest
{
public bool Html { get; set; } = false;
public Dictionary<string, HashSet<string>> Tags { get; set; }
}
The result was the same thing with a datetime field added just for the heck of it:
public class SearchResult : SearchRequest
{
public SearchResult(SearchRequest r)
{
this.Html = r.Html;
this.Tags = r.Tags;
}
public DateTime RequestedAt { get; set; } = DateTime.Now;
}
I Added a simple post method on the default HomeController.
[HttpPost]
public IActionResult Index([FromBody] SearchRequest searchRequest)
{
return new ObjectResult(new SearchResult(searchRequest));
}
I added a console Application to the solution to act as a client. I copied the two class definitions into that project.
I added this as the main method. Note you can either have the camel casing options on the request or not - asp.net accepted either.
static async Task Main(string[] _)
{
var tags = new[] { new { k = "brand", tags = new string[] { "bareminerals" } } }
.ToDictionary(x => x.k, v => new HashSet<string>(v.tags));
var request = new SearchRequest() { Html = true, Tags = tags };
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
var json = JsonSerializer.Serialize(request, options);
Console.WriteLine(json);
using (var client = new HttpClient())
{
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("http://localhost:59276", content);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<SearchResult>(data, options);
Console.WriteLine(data);
var keysSame = Enumerable.SequenceEqual(request.Tags.Keys, result.Tags.Keys);
var valuesSame = Enumerable.SequenceEqual(request.Tags.Values.SelectMany(x => x),
result.Tags.Values.SelectMany(x=>x));
Console.WriteLine($"Keys: {keysSame} Values: {valuesSame}");
}
}
This outputs:
{"html":true,"tags":{"brand":["bareminerals"]}}
{"requestedAt":"2020-10-30T19:22:17.8525982-04:00","html":true,"tags":{"brand":["bareminerals"]}}
Keys: True Values: True
I am trying to create a new user in my tenant using Microsoft Graph (v1.0) with help of the Microsoft doc.
When I create my user, I always get an error 400 bad request as response.
I am using HttpClient to make the post Request.
My Function :
private async Task<string> BuildUser(string token, string query)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
UserCreation uc = new UserCreation
{
accountEnabled = this.checkBoxActive.Checked,
displayName = this.textBoxDN.Text,
mailNickName = this.textBoxMail.Text,
passwordProfile = new PasswordProfile { forceChangePasswordNextSignIn = this.checkBoxChangeMDP.Checked, password = this.textBoxPassword.Text},
userPrincipalName = this.textBoxUPN.Text
};
string json = JsonConvert.SerializeObject(uc);
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = httpClient.PostAsync(query, content).Result;
return response.ToString();
}
My token is valid and i am able to make simple Get requests, my app have the authorizations mentioned here.
For example, my json var can contains :
{
"accountEnabled":true,
"displayName":"cyril testgraphh",
"mailNickName":"cyriltestgraphh",
"userPrincipalName":"cyriltestgraphh#mytenant.fr",
"passwordProfile":{
"forceChangePasswordNextSignIn":true,
"password":"XXX"
}
}
EDIT :
I solved my problem by using Microsoft Graph objects (Microsoft.Graph.User and Microsoft.Graph.PasswordProfile) and add .onmicrosoft.com to my upn
You should check your code. I tried the following code, it works well.
using Microsoft.Graph;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
UserCreation uc = new UserCreation
{
accountEnabled = true,
displayName = "cyril testgraphh",
mailNickName = "cyriltestgraphh",
passwordProfile = new PasswordProfile { ForceChangePasswordNextSignIn = false, Password = "Password!" },
userPrincipalName = "XXXXXX#jmaster.onmicrosoft.com"
};
string json = JsonConvert.SerializeObject(uc);
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = httpClient.PostAsync("https://graph.microsoft.com/v1.0/users", content).Result;
Console.Write(response);
Console.ReadLine();
}
}
class UserCreation
{
public bool accountEnabled { get; internal set; }
public string displayName { get; internal set; }
public string mailNickName { get; internal set; }
public string userPrincipalName { get; internal set; }
public PasswordProfile passwordProfile { get; internal set; }
}
}
And the response like this:
In my case the only thing returned from Azure Graph API was the error 400 - Bad Request... nothing else. :(
What I did to solve it? I was using the full blown Group object from Microsoft.Graph NuGet package to create a Group on Azure B2C. However, I was just using 5 properties of that object. While serializing to JSON it was serializing all properties of that class and for some reason the Graph API was barking about a malformed request.
So I just created an anonymous type with the properties that I needed:
var group = new
{
DisplayName = theGroupName,
Description = $"User group for {theGroupName}",
MailNickname = theGroupName.Replace(" ", string.Empty),
MailEnabled = false,
SecurityEnabled = true
};
After sending this streamlined object to the Graph API endpoint, I got a success response.
I am trying to validate an API response but I can't seem to understand how to use the content of the response.
This is my response :
"{\"On\": false, \"value\": null,}"
And I would like to test for the value of "On" (if its true then... or false then...).
Here is my code so far :
using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Net.Http.Headers;
namespace APITest
{
class Program
{
static void Main(string[] args)
{
PostRequest("My API");
Console.ReadKey();
}
async static void PostRequest(string url)
{
IEnumerable<KeyValuePair<string, string>> queries = new
List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("Test","1")
};
HttpContent q = new FormUrlEncodedContent(queries);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await
client.PostAsync(url,q))
{
using (HttpContent content = response.Content)
{
string mycontent = await
content.ReadAsStringAsync();
HttpContentHeaders headers = content.Headers;
Console.WriteLine(mycontent);
}
}
}
}
}
}
You create a class which represents that JSON structure and use a JSON serializor to de-serialize your string representation to an object of that class and use it as needed
Here i show you do it with JSON.NET
string mycontent = await content.ReadAsStringAsync();
var result= Newtonsoft.Json.JsonConvert.DeserializeObject<ApiResult>(mycontent);
if (result!= null)
{
if (result.On)
{
//do something, may be read, result.Value
}
else
{
//do something else
}
}
Assuming you have a class like this
public class ApiResult
{
public bool On { set; get; }
public string Value { set; get; }
}
I have to code a little C# Console application (but my knowledge on .NET is almost NULL) to make a POST request to a PHP API, for which I'm using an HttpClient instance. The API accepts a JSON like this
{
"User": {
"email": "email#host.com",
"password": "something"
},
"Establishment": {
"id": 147
}
}
After doing some research on how to do this what I've done so far is this:
static async Task RunAsync()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://italdelo.web.development.co/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// HTTP POST
Dictionary<string, string> user = new Dictionary<string, string>();
Dictionary<string, int> establishment = new Dictionary<string, int>();
HashTable postdata = new HashTable();
user.Add("email","email#host.com");
user.Add("password","something");
establishment.Add("id",147);
postdata.Add("User",user);
postdata.Add("Establishment",establishment);
HttpResponseMessage response = await client.PostAsJsonAsync("apiname/service", postdata);
response.EnsureSuccessStatusCode(); // Throw if not a success code.
}
catch (HttpRequestException e)
{
Console.WriteLine("Exception details: " + e.ToString());
}
}
}
Running this code I got this error message:
Severity Code Description Project File Line Suppression State Error CS0246 The type or namespace name 'HashTable' could not be found (are you missing a using directive or an assembly reference?) ProjectName C:\Users\...\Program.cs 47 Active
I know the error is the way I'm trying to setup the JSON to send to the API, which is a hashtable containing values (hashtable's key-value notation) as dictionaries. I'm pretty sure this is dumb but I don't really know how to setup this JSON for the API, I have no choice, I need to use C#. Can you help me please giving me some advice on how to fix this or get this done in another way? Thanks in advance.
you can create a class that will serve as parameter for consuming API.
//you classes
public class Parameter
{
public User user { get; set;}
public Establishment {get; set;}
}
public class User
{
public string email {get; set;}
public string password {get; set}
}
public class Establishment
{
public int id {get;set;}
}
//in your api calling method:
`var p = new Parameter
{
Establishment = new Establishment
{
id = 123
},
User = new User
{
email = "email",
password = "password"
}
};
HttpResponseMessage response = await client.PostAsJsonAsync("apiname/service", p);
Use c# anonymous classes
var payload = new{
User=new {
email= "email#host.com",
password= "something"
},
Establishment=new {
id= 147
}
}
then use newtonsoft json.net
var json = JsonConvert.SerializeObject(payload);
Looks like a typo, C# Hashtable does not capitalize the middle T:
https://msdn.microsoft.com/en-us/library/system.collections.hashtable(v=vs.110).aspx