HTTP Client
Unisave provides a thin wrapper around the .NET HttpClient
that lets you easily make HTTP requests to external web services.
Making requests
To make requests, you may use the Get
, Post
, Put
, Patch
and Delete
methods. An example GET
request is as easy as:
using Unisave.Facades;
var response = Http.Get("http://test.com");
You can then inspect the response:
// content
response.Body(); // string
response.Json(); // JsonObject
response.Form(); // Dictionary<string, string>
response.Bytes(); // byte[]
// status
response.Status; // int
response.IsOk; // true when status is 200
response.IsSuccessful; // true when status is 2xx
response.Failed; // true when status is 4xx or 5xx
response.IsClientError; // true when status is 4xx
response.IsServerError; // true when status is 5xx
// headers
response.Header("Content-Type"); // string
If the response is application/json
or application/x-www-form-urlencoded
, an indexer access can be used:
// get title of the latest news item for Team Fortress 2
return Http.Get(
"https://api.steampowered.com/" +
"ISteamNews/GetNewsForApp/v2/" +
"?appid=440&count=1"
)["appnews"]["newsitems"][0]["title"];
Request data
POST request with JSON body
It's common to send request body with a POST
request. The most common body type is a JSON object:
using LightJson;
var response = Http.Post(
"http://test.com/do-something",
new JsonObject {
["foo"] = "bar",
["baz"] = 42
}
);
Info: Methods
PUT
,PATCH
andDELETE
also support this approach.
GET request with query parameters
Instead of appending the query string directly to the URL (authough you still can), you can pass it as a dictionary via the second argument:
var response = Http.Get(
"http://test.com/query-something",
new Dictionary<string, string> {
["foo"] = "bar",
["baz"] = "42"
}
);
Info: Giving a
Dictionary<string, string>
as a second argument to other methods (POST
,PUT
,PATCH
andDELETE
) will send it as an form URL encoded body.
Specifying request body in advance
You can specify the request body before you send the request:
// JSON
var response = Http.WithJsonBody(
new JsonObject {
["foo"] = "bar",
["baz"] = 42
}
).Post("http://test.com/do-something");
// Form URL encoded
var response = Http.WithFormBody(
new Dictionary<string, string> {
["foo"] = "bar",
["baz"] = "42"
}
).Post("http://test.com/do-something");
Sending raw HttpContent
If you want to send a different body, you can always fallback to the .NET HttpContent class:
using System.Net.Http;
var response = Http.WithBody(
new StringContent(
"Hello!",
Encoding.UTF8,
"text/plain"
)
).Post("http://test.com/do-something");
Headers
You can add additional headers to the request using the WithHeaders
method:
var response = Http.WithHeaders(
new Dictionary<string, string>() {
["X-First"] = "foo",
["X-Second"] = "bar"
}
).Post("http://test.com/do-something");
Authentication
You may add authentication information via the HTTP basic authentication scheme or the OAuth 2.0 bearer token scheme:
var response = Http
.WithBasicAuth("username", "password")
.Post(...);
var response = Http
.WithToken("token")
.Post(...);
Error handling
Unisave HTTP client does not throw exceptions on client or server errors (4xx
or 5xx
status codes).
You can check for the error using the following properties:
response.Status; // int
response.IsOk; // true when status is 200
response.IsSuccessful; // true when status is 2xx
response.Failed; // true when status is 4xx or 5xx
response.IsClientError; // true when status is 4xx
response.IsServerError; // true when status is 5xx
If you, however, do want to throw an exception in such case, you can call the Throw
method. This method will, of course, not do anything if the request was successful.
var response = Http.Post(...);
response.Throw();
return response["foo"]["bar"];
The method will throw an instance of System.Net.HttpRequestException
.
The method also returns the response itself so it can be chained:
return Http.Post(...).Throw().Json()["foo"]["bar"];
The underlying .NET instance
Whenever you have an instance of a request or a response, you can access the respective HttpRequestMessage
and HttpResponseMessage
via the .Original
property:
var response = Http.Get(...);
response.Original.StatusCode; // 404
response.Original.ReasonPhrase; // "Not found"
Testing
This section is related to automated testing of your backend. Read the testing page to learn more.
The Http.Fake(...)
method allows you to return dummy responses when requests are made.
Faking responses
For example you can return an empty 200
status code response for every request made just by calling the Fake
method without any arguments:
Http.Fake();
var response = Http.Post(...);
You can also fake only a specific URL and specify the response to be returned:
Http.Fake("github.com/*", Http.Response(new JsonObject {
["foo"] = "bar"
}, 200));
Http.Fake(
"google.com/*",
Http.Response("Hello!", "text/plain")
);
You can also fake response headers:
Http.Fake("google.com/*",
Http.Response(
new JsonObject(),
200,
new Dictionary<string, string> {
["X-Header"] = "value"
}
)
);
Again, if you omit the URL address, it will fake all requests:
Http.Fake(
Http.Response(...)
);
Response sequences
Sometimes you want to return a sequence of responses from a URL. You can use the Http.Sequence()
method for that:
Http.Fake(
Http.Sequence()
.Push("Hello!", "text/plain", 200)
.Push(new JsonObject {...}, 200, headers)
.PushStatus(404)
);
When all the responses have been consumed, any furter requests will throw an exception. You may, however, specify a default response that should be returned when the sequence is empty, you may use the WhenEmpty
method:
Http.Fake(
Http.Sequence()
.Push(...)
.Push(...)
.WhenEmpty(Http.Response(...))
);
But if you want to return just a plain 200
empty response, you can simplify it to:
Http.Fake(
Http.Sequence()
.Push(...)
.Push(...)
.DontFailWhenEmpty()
);
Fake callback
If you require more complicated logic to determine what responses to return, you may pass a callback to the faking method. This callback will receive an instance of Unisave.Http.Client.Request
and should return a response instance.
Http.Fake("test.com/*"
request => {
string name = request["name"];
return Http.Response($"Hello {name}!");
}
);
Note: If the callback returns
null
, it will be ignored and the next callback in the row will be called. This continues until there are no more callbacks and then the request gets sent for real. All the faking methods described above actually just register callbacks under the hood.
Inspecting requests
When faking responses, you may sometimes want to inspect the requests that have been made in order to check that your backend is sending the correct data or headers. You may acomplish this by calling the Http.AssertSent
method after calling Http.Fake
.
The AssertSent
method accepts a callback which will be given an instance of a Unisave.Http.Client.Request
and it should return a boolean value indicating if the request matches your expectations. In order for the test to pass, at least one request must have been issued matching the given expectations:
Http.Fake();
Http.WithHeaders(new Dictionary<string, string> {
["X-First"] = "foo"
}).Post("http://test.com/players", new JsonObject {
["name"] = "Bob",
["coins"] = 123
});
Http.AssertSent(request =>
request.HasHeader("X-First", "foo") &&
request.Url == "https://test.com/players" &&
request["name"] == "Bob" &&
request["coins"] == 123
);
You may also assert that a specific request was not sent:
Http.AssertNotSent(request =>
request.Url == "http://test.com/foo"
);
Or you might want to check that no requests were sent at all:
Http.AssertNothingSent();