I have created a simple web api controller in mvc4 containing 4 methods (one for each CRUD operation). I'm able to use fiddler to test that the methods in my controller work.
I'm now trying to make a unit test to prove that these work. I've managed to serialize my client side object into json format, but now how do I use this string of json to actually invoke my methods?
If it helps, I am using Json.NET to serialize my client object - although I don't think this extention actually handles the delivery and retreival of it to the server.
Your unit tests should be written against the controller - so you don't need to make an actual HTTP request to unit test your Web API code, you just call the methods.
From a design perspective, if you want a restful Web API, the client should be able to send a standard HTTP message without having to serialize the request.
This is the kind of approach I have used to post an object to a restful Web API:
HttpResponseMessage response;
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://url_to_service");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseTask = client.PostAsJsonAsync("api/resource/somethingelse", someObjectToPost).Result;
responseTask.Wait();
response = responseTask.Result;
if (response.IsSuccessStatusCode)
{
var contentTask = response.Content.ReadAsAsync<SomeResponseType>();
contentTask.Wait();
SomeResponseType responseContent = contentTask.Result;
}
else
{
//Handle error.
}
In this case, someObjectToPost is your client-side object, though you can leave it to Web API to serialize it for you. In the above example I am assuming the reponse is of fictional type SomeResponseType - you can also use ReadAsStringAsync if the response is expected to be plain text.
The code presented here by nick_w is correct. You need to use HttpClient object. And as Steve Fenton mentioned, to create unit test you don't want to do it - rather test directly against controller. But for the functional test you can do it. I've done same thing. I've created helper class so I need only to call one of Http helper methods, depending if it is GET or POST, etc. that I do. This helper uses generic types so it operates with any types that being passed.
Related
I'm making an http request post to an external api. I am constructing a json object to add to the request body. How can I check if the added body/content is correct before it is sent.
public async void TestAuthentication()
{
var client = new HttpClient();
var request = new HttpRequestMessage()
{
RequestUri = new Uri("http://test"),
Method = HttpMethod.Post
};
var jsonObj = new
{
data = "eneAZDnJP/5B6r/X6RyAlP3J",
};
request.Content = new StringContent(JsonConvert.SerializeObject(jsonObj), Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
}
If you are not sure whether the serialization works as intended, you could give it a shot in LINQpad or dotnetfiddle.net. See my example that returns the JSON on the console. These tools are great for quick prototyping a method or a snippet, if you are not sure if a piece of code works as intended.
You could also check in Wireshark, but that could be a bit of an overkill and works best if your connection if not encrypted (no HTTPS).
I personally tend to test code that calls some API the following way:
Make the called URL parameterizable (via the classes constructor)
If there is any variable data this data should be passed as the methods parameter(s)
For your test start an HTTP server from your test fixture (read on testing with xUnit or NUnit if you don't know what this means)
I use PeanutButter.SimpleHTTPServer for that
Pass the local IP to the class that accesses the API
Check whether the HTTP server received the expected data
Whether or not this kind of code shall be tested (this way) may be debatable, but I found this way to work kind of good. I used to abstract the HttpClient class away, but IMHO I would not recommend this anymore, because if the class accesses the API (and does not do anything else, which is important), the HTTP access is the crucial part that shall be tested and not mocked.
I received a Postman json collection from an API vendor that works perfectly, but has something mystifying to me: The request is in a GET format, yet there is an x-www-form-urlencoded body.
URL: https://login.microsoftonline.com/d1e<secret>9563/oauth2/token
And when I look at the postman-generated c# code, the mystery continues:
var client = new RestClient("https://login.microsoftonline.com/d1e...d3/oauth2/token");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", "c06bb...79");
request.AddParameter("client_secret", "7~u...D");
request.AddParameter("resource", "https://vault.azure.net");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Note the AddParameter constructions for a GET call. To me, this must be a slight-of-hand for merely adding those values to the querystring. But when I look at the postman console I see:
In the postman console I would have expected to see those params appended to the url as a querystring, and then everything would have made sense. But you can see that it's a bonafide Request Body.
When I make GET calls in my c# code I like to use the simple yet solid WebClient object to call the DownloadString() method. But this method is only for GETs and there's no way to send a form-post style body, understandably.
Is postman truly sending a GET with all those values being appended to the url as a querystring? And should I do the same in my DownloadString() call? Or is there something else going on here? Should I instead, in my c#, be calling the UploadString() method and sending a form post BODY as a GET??
Http protocol supports adding a body to a request, but the WebClient class you use doesn't. Presumably because it isn't considered the norm.
I'm sure there's good reasons for Microsoft using it in the OAuth flow though. Those guys normally do things right!
HTTP GET with request body
API is just an abstraction , you can send what ever you want to the API . It depends on the implementation , how the server handles these data.
Some services considers only what it requires and ignores other information
some services considers the entire requests and validates that it has only the allowed data. what should be allowed depends on the service
Postman is just a client that sends data to server , its upto you to decide what all information it should send . If you dont need any body then keep it as none. if you need some thing then add it.
I am using C#
in my code, I call API
This API returns a string.
But the problem is when I read the API it comes with backslashes and quotations.
So if the returned value is Black\White's colors
It becomes "\"Black\\\\White's colors\""
or empty string is "\"\""
I wonder if there is a way to parse string the in a right way.
here is my code
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string str = client.GetStringAsync(url).Result;
Like this
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string str = await client.GetStringAsync(url);
//choose this if using NewtonsoftJson
string parsedWithNewtonsoft = JsonConvert.DeserializeObject<string>(str);
//choose this if using STJ
string parsedWithSystemTextJson = JsonSerializer.Deserialize<string>(str);
Choose one of the last two lines according to your preferred installed Json parser (manage nuget packages for the project and install either newtonsoft or system.text.json)
Don't use Result; use async proper (which means you have to make your method calls async all the way up the chain). If you strive to turn all your async calls synchronous then your app will spend a lot of tine sitting around doing nothing, waiting for network io to finish, when that time could be put to doing other useful work
Also, HttpClient is not supposed to be created anew every time you want to use it. Either configure your DI with services.AddHttpClient(); passing in options as necessary or if you aren't using DI/not writing a service of your own, you can get some ideas on how to use HttpClientFactory to manufacture HttpClients for you form this question - you may even be able to configure the default request headers as part of the manufacture so you don't need to set them in code
Finally, I should point out that I think there's a chance you're doing a lot more work than you need to if your API you're using publishes a swagger document; if it does you can use a tool like AutoRest (or the "add rest client" feature of visual studio, which uses AR), NSwag, WebApiClientGen etc and tell it "here is the api I want to use" and it'll make everything you need to call the api and return c# objects; you don't need to code any of this low level stuff, pushing data around and parsing responses, yourself
In the past I made a class that shunk the request on an endpoint. Now, I create a dll that include this method, this is the code that I'm trying to convert on this library:
using (var client = new HttpClient())
{
string requestJson = JsonConvert.SerializeObject(data);
client.DefaultRequestHeaders.Add("token", token);
byte[] responseArray = client. 'there is no upload data method
// the bottom code is of the old method
byte[] responseArray = client.UploadData(requestURI, method, Encoding.UTF8.GetBytes(requestJson));
return Encoding.ASCII.GetString(responseArray);
}
In the not portable library System.Net I can call client.UploadData, but here I see only : postAsync and putAsync, there is a method that independent from the put or post request allow me to send the data from the client to the server? Thanks in advance.
In your old code you used some method passed in method parameter to send data with UploadData method, and it was probably POST or PUT. If you do not specify the method for UploadData, POST is being used. So you should use PostAsyncor PutAsync, based on you current code and the value of method parameter you pass to UploadData.
The simplest way would be to use something like this:
using(var client = new HttpClient())
{
var response = await client.PostAsJsonAsync(requestUrl, data);
return await response.Content.ReadAsStringAsync();
}
The code for PUT would be the same, but with PutAsJsonAsync
In an HTTP request PUT and POST are the correct ways to transmit data to a server, it does not make sense to send data independently of these methods. When you are using a client such as that available in System.Net this is merely being abstracted away from you.
I'm using the sendgrid api here:
https://sendgrid.com/docs/API_Reference/Web_API_v3/Marketing_Campaigns/contactdb.html#Delete-a-Recipient-DELETE
and it shows passing an array of strings to the DELETE call. When I look at the signature of System.Net.Http.HttpClient, DELETE does not allow for content to be passed in.
Is there a standard around DELETE that does not allow for multiple content passed at the same time?
API definition:
The HTTP/1.1 RFC states that a DELETE request's payload has no defined semantics.
It's not illegal to include a payload, but this means that if a payload is included, it should be ignored.
Many HTTP clients, such as the one provided by the .NET framework, don't provide an interface to include a payload when it has no defined semantics for the method.
Unfortunately, many REST APIs do require a payload with these methods. You can accomplish this by manually creating a HttpRequestMessage object, setting the Method and Content properties, and passing it to the HTTP client's SendAsync method.
Create an extension method
public static class HttpClientExtensions
{
public static Task<HttpResponseMessage> Delete(this HttpClient client, HttpContent content)
{
var request = new HttpRequestMessage { Method = "DELETE", Content = content);
return client.SendAsync(request);
}
}
However I cannot recommend it, as it breaks basic assumptions of HTTP, which allows efficient HTTP Proxies to work.
The "correct method" around this problem is to use HTTP 2.0 (or HTTP 1.1 Pipelining, which is deprecated due to it being mostly broken, but you could try it out) to create multiple DELETE requests. In theory that solution does not require any code change.