I'm working on a proof of concept of a RESTful API service for my employer, but have found myself slightly stuck.
I've got it working on GET-requests, both getting all the data from a view and getting some data based on parameters (id). However since the stored procedure has a TONNE of fields (140 columns) I'm a bit at a loss about how to handle any inserts / update statements that would have equally as many rows in their queries.
I've been looking around for solutions to handling multiple parameters, but I fail to see the benefit of using a "?param=args" per row. Is there a way to make the URL look slightly more formatted by passing in objects or something of the sort? If so, how would you go about (stress)testing that?
Any suggestions and advice would be greatly appreciated! :)
Example of passing data in the request body:
Creating your body content:
var example = "Hello World";
Dictionary<string, string> pairs = new Dictionary<string, string>();
pairs.Add("ExampleData", example);
var formContent = new FormUrlEncodedContent(pairs);
var response = await PostAsync(YourRouteToRestApi, formContent);
The Post method would look something like that:
public async Task<string> PostAsync(string apiCall, HttpContent data)
{
var client = new HttpClient();
try
{
var response = await client.PostAsync(apiCAll, data);
return await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
// do something
}
}
Now you have a clean url, without hundreds of ?param=args.
If it is possible to encapsulate all your paramater into an object, or if you create a Collection which holds your parameters (like Souvik Ghosh suggested). You could pass this object or collection in your PosAsync method instead of the Dictionary data.
So the HttpClient will take care of the Json serialization.
In your WebApi you would get this data like this:
public void Post([FromBody]YourObject stuff)
{
// do something
}
Related
I have used the following code to retrieve the content of a JSON feed and as you see I have used the paging techniques and Skip and Take methods like this:
[HttpGet("[action]")]
public async Task<myPaginatedReturnedData> MyMethod(int page)
{
int perPage = 10;
int start = (page - 1) * perPage;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("externalAPI");
MediaTypeWithQualityHeaderValue contentType =
new MediaTypeWithQualityHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(contentType);
HttpResponseMessage response = await client.GetAsync(client.BaseAddress);
string content = await response.Content.ReadAsStringAsync();
IEnumerable<myReturnedData> data =
JsonConvert.DeserializeObject<IEnumerable<myReturnedData>>(content);
myPaginatedReturnedData datasent = new myPaginatedReturnedData
{
Count = data.Count(),
myReturnedData = data.Skip(start).Take(perPage).ToList(),
};
return datasent;
}
}
My paging works fine, however I can't see any performance improvement and I know this is because every time I request a new page it calls the API again and again and after retrieving all contents, it filters it using Skip and Take methods, I am looking for a way to apply the Skip and Take methods with my HttpClient so that it only retrieves the needed records for every page. Is it possible? If so, how?
In order to apply the Take/Skip to the data retrieval, the server would have to know about them. You could do that with an IQueryable LINQ provider (see [1] for getting only an idea of how complex that is) or, better, by passing the appropriate values to the client.GetAsync call, something like
HttpResponseMessage response = await client.GetAsync(client.BaseAddress + $"?skip={start}&take={perPage}");
Of course, your server-side code has to interpret those skip and take parameters correctly; it's not automatic.
You might also want to look at OData (see [2]), but I have never actually used it in production; I just know it exists.
[1] https://msdn.microsoft.com/en-us/library/bb546158.aspx
[2] https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/calling-an-odata-service-from-a-net-client
My question may be trivial but I have spent almost 6hrs just trying things out.
public async Task<object> save()
{
var uri = "https://newsapi.org/v1/articles?source=talksport&apiKey=longKey";
var httpClient = new HttpClient ();
HttpResponseMessage res = await httpClient.GetAsync(uri);
var data = await res.Content.ReadAsStreamAsync();
// this is what I want to achieve like in python you can do something like this
foreach(var item in data){
Console.writeline(item.summary);
}
// end of arbitrary code
return data;
}
My problem is ,am unable to do this conversion to get the response and then accessing the json data.
In python you can do something
r = request.get(apiUrl)
data = r.json()
for item in data:
print(item.summary)
This is all I have struggle to achieve with c#, Any help to complete the code or explanation. Thanks
Try to use something like this:
Install Newtonsoft.Json package and add using Newtonsoft.Json;
using (var request = new HttpRequestMessage()) {
request.RequestUri = new Uri("https://newsapi.org/v1/articles?source=talksport&apiKey=longKey");
request.Method = HttpMethod.Get;
using (var response = await httpClient.SendAsync(request)) {
string content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<IList<dynamic>>(content);
foreach(var item in result){
Console.writeline(item.summary);
}
}
}
From comment
Then i get this
"{\"vouchers\":[\"UN9NKK\",\"FYMFVS\",\"WV5AX7\",\"M2TJJ8\",\"FBB9AL\",\"MBW8Z4\"]}"
You can create a new class
public class MyResponse {
public IEnumerable<string> Vouchers {get;set; }
}
then
var response = JsonConvert.DeserializeObject<MyResponse>(content);
foreach(var item in response.Vouchers){
Console.WriteLine(item);
}
If you don't mind a small library dependency, Flurl (disclaimer: I'm the author) gets you Python's simplicity in C#:
var data = await apiUrl.GetJsonAsync();
In this case, data is a C# dynamic type, which means you can access all the JSON object's properties by name without defining a corresponding C# class, much like an untyped language. If you do want to declare a class and get compile-time type checking, that works with Flurl too:
var data = await apiUrl.GetJsonAsync<MyClass>();
Now data is an instance of MyClass instead of a dynamic.
Get Flurl.Http on Nuget, and reference it with using Flurl.Http;.
I am currently developing a simple c# formflow bot that captures the values and sends those values off to an external api, gets the json data back from the external api and creates Card Attachments based on the results returned. I am making the call to the external api in the OnCompletion delegate as follows, To keep it simple I am not passing any values to the api (For testing purposes)
.OnCompletion(async (context, profileForm) =>
{
var reply = context.MakeMessage();
var carsFromApi = await GetCarsAsync("/api/values");
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments = GetCards(carsFromApi);
await context.PostAsync(reply);
// Tell the user that the form is complete
})
I make the call to the api and store the results in "carsFromApi" , I step into that which is the following code snippet
private static async Task<List<Car>> GetCarsAsync(string path)
{
List<Car> car = new List<Car>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
car = await response.Content.ReadAsAsync<List<Car>>();
}
return await response.Content.ReadAsAsync<List<Car>>();
}
Problem is when I press F10 and go to the next line which is "reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;". The local variable that stored the cars "carsFromApi " is now null. This is the part where it all falls over. I cant pass this "carsFromApi" to "reply.Attachments = GetCards(carsFromApi);" I have tried to store the data in a private variable but that also seems to be null. The external api is working because it just returns a list of static text for now. Any ideas? Thanks in advance.
Based on what you are describing it sounds that your code is not existing through the path of the if (response.IsSuccessStatusCode). Check if that point is reached as I suspect an exception or something is going wrong with the request.
Alternatively, you can try doing the request in the ResumeAfter<T> method you specified when calling the Form instead of that in the OnCompletion delegate
I am very new to MVC and making api calls server side and need a little guidance. I have created a simple method to call an api to retrieve results in a JSON object:
apiController.cs (normal controller.cs file)
[HttpGet]
public JsonResult getDefaultStuff(string a = "abc") {
var url = "https://myapiurl";
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd("Blah");
var response = client.GetStringAsync(url);
return Json(response, JsonRequestBehavior.AllowGet);
}
The results return in an array like this:
{Result: {examples: [[0000,6.121],[0000,1.122],[0000,9.172]]},"Id":81,"Exception":null,"Status":5,"IsCanceled":false,"IsCompleted":true,"CreationOptions":0,"AsyncState":null,"IsFaulted":false}
I need it to return with keynames like this :
{
"examples": [
{
"Keyname1": "45678",
"Keyname2": "1234"
},
{
"Keyname1": "14789",
"Keyname2": "1234"
},
{
"Keyname1": "12358",
"Keyname2": "4569"
}
]
}
Do I need to use IDictonary? I am unsure of the approach. Do I create a new object and then loop through each result adding keynames? an example would be much appreciated or just the approach will be very helpful.
You can do the following:
By using the Json.Net nuget package first deserialize the response into, for example, an anonymous object:
var deserialized = JsonConvert.DeserializeAnonymousType(response, new
{
examples = new[] { new decimal[] { } }
});
Then transform this object into the new one that has the property structure you need:
var result = new
{
examples = deserialized.Result.examples.Select(x => new
{
Keyname1 = x[0],
Keyname2 = x[1]
})
};
And return it to the client:
return Json(result, JsonRequestBehavior.AllowGet);
This is what your solution could roughly look like but you do have to keep several things in mind though:
Exception handling for deserialization
Checks for possible nulls in the deserialized object
Maybe also the safer way of retrieving values from the array to avoid possible out of range exceptions.
Also the GetStringAsync method is asynchronous and you should put an await keyword in front of it, but in order to do so you need to make your method async as well:
public async Task<JsonResult> getDefaultStuff(...)
If you don't have enough knowledge of asynchronous programming, here is the most advanced, in-depth and comprehensive video explaining it from top to bottom I have ever seen, so check it out whenever you find time...
Could someone please explain to me how the request and response model works in WCF? I have a simple service that exposes a method called getRateOfExchange with a single integer parameter that returns rateOfExchange[], but the Reference.cs file that is generated from the service contains lots of classes:
getRateOfExchange (seems to be the parameters)
getRateOfExchangeRequest
getRateOfExchangeResponse
I have tried every permutation of these classes and their methods but nothing works. Intuitively you would expect to create a request object with the parameter object as a parameter, and then pass this request to the method on the response that executes the request to the sever.
But no.
It has to be painful.
Can someone please explain?
UPDATE
Thank you Gigi, but my classes don't look like that.
If I follow your model, my request would look like this:
CharterServices.charterServiceClient proxy = new CharterServices.charterServiceClient();
using (OperationContextScope scope = new OperationContextScope(proxy.InnerChannel));
{
using (proxy as IDisposable)
{
var response = proxy.getRateOfExchange()
}
}
However, my getRateOfExchange() method requires a getRateOfExchange object, so the above code doesn't compile.The getRateOfExchange class contains parameters that are the parameters to the proxy.getRateOfExchange() method. I have tried creating an instance of this class and passing it the above method, like this:
using (proxy as IDisposable)
{
var rateOfExchange = new Service.getRateOfExchange()
{
charterEnquiryId = 1550003668
};
using (OperationContextScope scope = new OperationContextScope(proxy.InnerChannel));
{
using (proxy as IDisposable)
{
var response = proxy.getRateOfExchange(rateOfExchange);
foreach (var rateOfExcchange in response)
{
Debug.WriteLine(rateOfExcchange.fromCurrencyName);
}
}
}
}
but it hangs when trying to call getRateOfExchange().
Aaargh! I know the service is working because I can execute a request in SoapUI to the same WSDL.
Can you help?
It's actually not painful at all. Once you generate the proxy/client classes, you just create an instance of the client and then call the methods as if they were local method calls.
I can't explain the whole process here, but I'll instead refer you to the intro I wrote over a year ago which explains the whole process in terms of a simple example.
You can test the service using the WCF Test Client even before you've written your own client. Writing the client is very easy if you use the Service References.
Here's an excerpt from the code from that blog post illustrating how to use client code, modified to have a using block and use the var keyword for brevity:
static void Main(string[] args)
{
using (var service = new ServiceReference1.Service1Client())
{
var response = service.GetData(5);
Console.WriteLine(response);
Console.ReadLine();
}
}
The system was throwing an exception which was not being caught, so the component model decided to hang! Fixed it now.
Suppose rateOfExchange is a List of integers, I have just added 10 numbers to it, from 1 to 10.
Then this list is sent as a parameter to the getRateOfExchange method of the service client object.
List<int> rateOfExchange=new List<int>();
for(int i=0;i<10;i++)
{
rateOfExchange.Add(i);
}
//Service Call
ServiceClient obj=new ServiceClient();
var response=obj.getRateOfExchange(rateOfExchange);
foreach(var item in response)
{
Console.WriteLine(item);
}
Console.ReadLine();
Hope it helps.