Is it ok to use generic method for web api? - c#

I am getting data from a web api by making httpclient calls from various MVC controllers. Because I have to do it many times, I made a generic method that I can reuse by just passing in the api url and the model return type. It works fine, but I am concerned I am loosing the oppurtunity to have different methods, like GetPeople, GetPersonById, etc. Is there a downside to what I am doing?
Utilities.cs:
public static T GetDataFromWebService<T>(T model, string svcEndPoint)
{
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(svcEndPoint).Result;
var result = response.Content.ReadAsAsync<T>().Result;
return result;
}
Controller:
string svc = appSettings.GetPeopleApiUrl;
var model = new List<Person>();
var people = Utilities.GetDataFromWebService <IEnumerable<Person>>(model, svc);

You can still have specialized methods such as GetPeople, GetPersonById by layering them on top:
PeopleModel GetPeople(...) {
return GetDataFromWebService<PeopleModel>(...);
}
No downsides, it is good that you have all boilerplate code in a shared utility method.

Well, there is definitely, better way of doing the overall implementation, but if I have to stick to the question, I would say any attempt of reducing coupling is a good step for future directions. In your situation, since you are abstracting away the responsibility of making service calls to a utility method, it would help you in the long run.
Though I would suggest that instead of having this stuffed together in Utility class you should make the connectivity it's own class, something like this
public delegate T ParseToObject<T>(string response);
public class ServiceConnector : IServiceConnector
{
public string LogoffUrl { get; set; }
public bool SupportRetry { get; set; }
private WebClient _client;
public ServiceConnector()
{
}
public T GetResponse<T>(string requestUrl, ParseToObject<T> parsingMethod)
{
string response = __getResponse(requestUrl);
return parsingMethod(response);
}
private string __getResponse(string requestUrl)
{
string serviceResponse = string.Empty;
try
{
__initializeWebClient();
Logger.Current.LogInfo(string.Format("Sending request with URL {0}", requestUrl));
serviceResponse = _client.DownloadString(requestUrl);
}
catch (Exception ex)
{
if (ex.Message != null)
{
Logger.Current.LogException(string.Format("Exception during OvidWS request {0} ", requestUrl), ex);
_client = null;
}
//Sample implementation only, you could throw the exception up based on your domain needs
}
return serviceResponse;
}
private void __initializeWebClient()
{
if (_client == null)
_client = new WebClient();
}
}
With this in place, tomorrow, let's say you want to add support to log off, support cookies, support credentials, support retries, this is the only place where you can be and comfortably make changes. Similarly if you want to use Webclient over something else, you can also do that better here.

Try with that helper:
public static class WebClientExtension
{
public static T DownloadSerializedJsonData<T>(string url) where T : new()
{
var contentType = ConfigurationManager.AppSettings["ContentType"];//content type in app config or web config
using (var webClient = new WebClient())
{
webClient.Headers.Add("Content-Type", contentType);
var jsonData = string.Empty;
try
{
jsonData = webClient.DownloadString(url);
}
catch (Exception ex)
{
throw ex;
}
return !string.IsNullOrEmpty(jsonData) ? JsonConvert.DeserializeObject<T>(jsonData) : new T();
}
}
public static T AuthorizationContentSerializedJsonData<T>(string url) where T : new()
{
string jsonData = null;
try
{
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
//ClientBase.AuthorizeRequest(httpRequest, Authorization.AccessToken);
var response = httpRequest.GetResponse();
Stream receiveStream = response.GetResponseStream();
var readStream = new StreamReader(receiveStream, Encoding.UTF8);
jsonData = readStream.ReadToEnd();
response.Close();
}
catch (Exception ex)
{
throw ex;
}
return !string.IsNullOrEmpty(jsonData) ? JsonConvert.DeserializeObject<T>(jsonData) : new T();
}
}
App confing / Web config example for content type
<add key="ContentType" value="application/hal+json; charset=UTF-8" />

Related

Refactoring code to allow for unit testing of HttpClient

I am dealing with a piece of code that looks like this:
public class Uploader : IUploader
{
public Uploader()
{
// assign member variables to dependency injected interface implementations
}
public async Task<string> Upload(string url, string data)
{
HttpResponseMessage result;
try
{
var handler = new HttpClientHandler();
var client = new HttpClient(handler);
result = await client.PostAsync(url, new FormUrlEncodedContent(data));
if (result.StatusCode != HttpStatusCode.OK)
{
return "Some Error Message";
}
else
{
return null; // Success!
}
}
catch (Exception ex)
{
// do some fancy stuff here
}
}
}
I am trying to unit test the Upload function. In particular, I need to mock the HttpClient. After reading the other answers on here and these two articles, I know that one of the better ways to solve this is to mock the HttpMessageHandler instead and pass that to HttpClient and have it return whatever I want.
So, I started along that path by first passing in HttpClient in the constructor as a dependency:
public class Uploader : IUploader
{
private readonly HttpClient m_httpClient; // made this a member variable
public Uploader(HttpClient httpClient) // dependency inject this
{
m_httpClient = httpClient;
}
public async Task<string> Upload(string url, string data)
{
HttpResponseMessage result;
try
{
var handler = new HttpClientHandler();
result = await m_httpClient.PostAsync(url, new FormUrlEncodedContent(data));
if (result.StatusCode != HttpStatusCode.OK)
{
return "Some Error Message";
}
else
{
return null; // Success!
}
}
catch (Exception ex)
{
// do some fancy stuff here
}
}
}
and adding: services.AddSingleton<HttpClient>(); to the ConfigureServices method of Startup.cs.
But now I face a slight issue where the original code specifically creates a HttpClientHandler to pass in. How then do I refactor that to take in a mockable handler?
I find the simplest way is to continue using HttpClient, but pass in a mocking HttpClientHandler such as https://github.com/richardszalay/mockhttp
Code sample from the link above:
var mockHttp = new MockHttpMessageHandler();
mockHttp.When("http://localhost/api/user/*")
.Respond("application/json", "{'name' : 'Test McGee'}");
// Inject the handler or client into your application code
var client = mockHttp.ToHttpClient();
var response = await client.GetAsync("http://localhost/api/user/1234");
var json = await response.Content.ReadAsStringAsync();
Console.Write(json); // {'name' : 'Test McGee'}
The Dependency Injection framework built into .NET Core ignores internal constructors, so it will call the parameter-less constructor in this scenario.
public sealed class Uploader : IUploader
{
private readonly HttpClient m_httpClient;
public Uploader() : this(new HttpClientHandler())
{
}
internal Uploader(HttpClientHandler handler)
{
m_httpClient = new HttpClient(handler);
}
// regular methods
}
In your unit tests, you can use the constructor accepting the HttpClientHandler:
[Fact]
public async Task ShouldDoSomethingAsync()
{
var mockHttp = new MockHttpMessageHandler();
mockHttp.When("http://myserver.com/upload")
.Respond("application/json", "{'status' : 'Success'}");
var uploader = new Uploader(mockHttp);
var result = await uploader.UploadAsync();
Assert.Equal("Success", result.Status);
}
Normally I'm not a big fan of having an internal constructor to facilitate testing, however, I find this more obvious and self-contained than registering a shared HttpClient.
HttpClientFactory might be another good option, but I haven't played around with that too much, so I'll just give info on what I've found useful myself.
One way would be to abstract your HTTP functionality into a service i.e. HttpService which implements an interface of IHttpService:
IHttpService
public interface IHttpService
{
Task<HttpResponseMessage> Post(Uri url, string payload, Dictionary<string, string> headers = null);
}
HttpService
public class HttpService : IHttpService
{
private static HttpClient _httpClient;
private const string MimeTypeApplicationJson = "application/json";
public HttpService()
{
_httpClient = new HttpClient();
}
private static async Task<HttpResponseMessage> HttpSendAsync(HttpMethod method, Uri url, string payload,
Dictionary<string, string> headers = null)
{
var request = new HttpRequestMessage(method, url);
request.Headers.Add("Accept", MimeTypeApplicationJson);
if (headers != null)
{
foreach (var header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
}
if (!string.IsNullOrWhiteSpace(payload))
request.Content = new StringContent(payload, Encoding.UTF8, MimeTypeApplicationJson);
return await _httpClient.SendAsync(request);
}
public async Task<HttpResponseMessage> Post(Uri url, string payload, Dictionary<string, string> headers = null)
{
return await HttpSendAsync(HttpMethod.Post, url, payload, headers);
}
}
Add to your services:
services.AddSingleton<IHttpService, HttpService>();
In your class you would then inject IHttpService as a dependency:
public class Uploader : IUploader
{
private readonly IHttpService _httpService; // made this a member variable
public Uploader(IHttpService httpService) // dependency inject this
{
_httpService = httpService;
}
public async Task<string> Upload(string url, string data)
{
HttpResponseMessage result;
try
{
result = await _httpService.PostAsync(new Uri(url), data);
if (result.StatusCode != HttpStatusCode.OK)
{
return "Some Error Message";
}
else
{
return null; // Success!
}
}
catch (Exception ex)
{
// do some fancy stuff here
}
}
}
You could then use Moq to mock HttpService in your unit test:
[TestClass]
public class UploaderTests
{
private Mock<IHttpService> _mockHttpService = new Mock<IHttpService>();
[TestMethod]
public async Task WhenStatusCodeIsNot200Ok_ThenErrorMessageReturned()
{
// arrange
var uploader = new Uploader(_mockHttpService.Object);
var url = "someurl.co.uk";
var data = "data";
// need to setup your mock to return the response you want to test
_mockHttpService
.Setup(s => s.PostAsync(url, data))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError));
// act
var result = await uploader.Upload(new Uri(url), data);
// assert
Assert.AreEqual("Some Error Message", result);
}
[TestMethod]
public async Task WhenStatusCodeIs200Ok_ThenNullReturned()
{
// arrange
var uploader = new Uploader(_mockHttpService.Object);
var url = "someurl.co.uk";
var data = "data";
// need to setup your mock to return the response you want to test
_mockHttpService
.Setup(s => s.PostAsync(new Uri(url), data))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK));
// act
var result = await uploader.Upload(url, data);
// assert
Assert.AreEqual(null, result);
}
}

Error handling (Sending ex.Message to the client)

I have an ASP.NET Core 1.0 Web API application and trying to figure out how to pass the exception message to the client if a function that my controller is calling errors out.
I have tried so many things, but nothing implements IActionResult.
I don't understand why this isn't a common thing that people need. If there truthfully is no solution can someone tell me why?
I do see some documentation out there using HttpResponseException(HttpResponseMessage), but in order to use this, I have to install the compat shim. Is there a new way of doing these things in Core 1.0?
Here is something I have been trying with the shim but it isn't working:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
Customer c = _customersService.GetCustomerById(id);
if (c == null)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response);
//return NotFound();
}
return new ObjectResult(c);
}
When the HttpResponseException is thrown, I look on the client and can't find the message I am sending anything in the content.
Here is an simple error DTO class
public class ErrorDto
{
public int Code {get;set;}
public string Message { get; set; }
// other fields
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And then using the ExceptionHandler middleware:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
context.Response.ContentType = "application/json";
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
var ex = error.Error;
await context.Response.WriteAsync(new ErrorDto()
{
Code = <your custom code based on Exception Type>,
Message = ex.Message // or your custom message
// other custom data
}.ToString(), Encoding.UTF8);
}
});
});
Yes it is possible to change the status code to whatever you need:
In your CustomExceptionFilterAttribute.cs file modify the code as follows:
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new ContentResult
{
Content = $"Error: {exception.Message}",
ContentType = "text/plain",
// change to whatever status code you want to send out
StatusCode = (int?)HttpStatusCode.BadRequest
};
}
}
That's pretty much it.
If you have custom exceptions, then you can also check for them when grabbing the thrown exception from the context. Following on from that you can then send out different HTTP Status Codes depdending on what has happened in your code.
Hope that helps.
You can create a custom Exception Filter like below
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new JsonResult(exception.Message);
}
}
Then apply the above attribute to your controller.
[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
throw new Exception("Suckers");
return new string[] { "value1", "value2" };
}
}
Rather than raising and catching an exception, how about you simplify your action to:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
var customer = _customersService.GetCustomerById(id);
if (customer == null)
{
return NotFound("Customer doesn't exist");
}
return Ok(customer);
}
I wrote a blog post with some more options such as returning a JSON object instead of text.
Maybe that is helpful. You can return just object and sent for example a BadRequest (HTTP CODE: 400) with your custom object as actual parameter (I just used an interpolated string here) but you can put in anything.
In your client side you can catch that error situation for example with an AJAX error handler.
// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return truckFahrerGeoDataItems;
}
Or an even more cleaner way with IActionResult like that way:
// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return Ok(truckFahrerGeoDataItems);
}
Late to the party but refining the answer .
Define your error response class with minimum below attributes
using Microsoft.AspNetCore.Http;
public class ErrorResponse
{
private readonly RequestDelegate next;
public ErrorResponse(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError;
string result = string.Empty;
object data = new object();
if (ex is ForbiddenException)
{
code = HttpStatusCode.Forbidden;
result = JsonConvert.SerializeObject(new Response<object>(Status.Forbidden(ex.Message), data));
}
else if(ex is BadRequestException){
code = HttpStatusCode.BadRequest;
result = JsonConvert.SerializeObject(new Response<object>(Status.BadRequest(ex.Message), data));
}
else if (ex is NotFoundException)
{
code = HttpStatusCode.NotFound;
result = JsonConvert.SerializeObject(new Response<object>(Status.NotFound(ex.Message), data));
}
else if (ex is UnauthorizedException)
{
code = HttpStatusCode.Unauthorized;
result = JsonConvert.SerializeObject(new Response<object>(Status.Unauthorized(ex.Message), data));
}
else
{
result = JsonConvert.SerializeObject(new Response<object>(Status.InternalServerError(ex.Message), data));
}
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
Next use this class as middleware in startup.cs class
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorResponse));
Now each request and response will go through this class,if an error occurs then error code will be set to true with error code. A sample response like below
data: {}
status: {
code: 404
error: true
message: "No employee data found"
type: "Not Found"
}
I had the same problem and after some research, I found out I could use HttpClient to call my API and read the response easily. HttpClient does not throw any error when the HTTP response contains an error code, but it sets the IsSuccessStatusCode property to false.
This is my function using the HttpClient. I call this from my controller.
public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
{
string uri = apiUrl + url;
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));
return response;
}
}
This is my controller code, where I call the function and read the response and determine whether I have an error or not and respond accordingly. Note that I am checking the IsSuccessStatusCode.
HttpResponseMessage response;
string url = $"Setup/AddDonor";
var postdata = JsonConvert.SerializeObject(donor);
response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
//var headers = response.Headers.Concat(response.Content.Headers);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = true,
message = tnxresult.Message,
statusCode = tnxresult.StatusCode
});
}
else
{
ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = false,
message = rs.Message,
statusCode = rs.StatusCode
});
}
My API returns error messages in JSON. If the call is successful, I am packing the response in JSON too.
The crucial line of code is this one...
var responseBody = await response.Content.ReadAsStringAsync();
It serializes the HTTP content to a string as an asynchronous operation.
After that I can convert my JSON string to an object and access the error/success message and the Status Code too.

Pass dynamic object to common method for Post data in Application

In my application, it is calling webapi, and application is in MVC5. Now I have many more methods in application project. So I want to create one common(global) method to call the api. Methods which are used for get content those are working fine, but methods, those are used to post(means save data to DB kind of) data, getting error like this. Code is like below:
public static string SendDataToAPI(dynamic objCommon, string urlParameters, ref string errorMessage)
{
try {
string url = ConfigurationSettingHelper._BaseUrl;
string strJson = string.Empty;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsJsonAsync(urlParameters, objCommon);
if (response.Result.IsSuccessStatusCode)
{
strJson = response.Result.Content.ReadAsStringAsync().Result;
}
else {
errorMessage = response.Result.Content.ReadAsStringAsync().Result;
}
return strJson;
}
}
catch (Exception ex) {
errorMessage = ex.Message;
return errorMessage;
}
}
So, my question is how can I pass dynamic object type in PostAsJsonAsync method? How can I set this kind of method for common use?
Try something below where the post data model generically accepts any type and complex object also should be wrapped into single model.
public async virtual Task<string> ExecuteHttpApiCall<TModel>(TModel model, string url)
{
HttpResponseMessage response;
using (var client = new HttpClient())
{
if (model != null)
{
response = await client.PostAsJsonAsync(url, model);
}
else
{
response = await client.GetAsync(url);
}
return await response.Content.ReadAsStringAsync();
}
}

Converting the content of HttpResponseMessage to object

My Question: How do I do this?
So, I hadn't touched anything .Net in about 6 years until this week. There's a lot that I've forgotten and even more that I never knew and while I love the idea of the async/await keywords, I'm having a slight problem implementing the following requirements for a client's API implementation:
The ServerAPI class has a method for each of the API methods, taking appropriate input parameters (e.g. the method Login takes in an id and a password, makes the API call and returns the result to the caller).
I want to abstract away the JSON so that my API methods return the actual object you're fetching (e.g. the Login method above returns a User object with your auth token, uid, etc.)
Some API methods return a 204 on success or no meaningful content (not meaningful in my usecase maybe I only care about success/failure), for these I'd like to return either a bool (true = success) or the status code.
I'd like to keep the async/await (or equivalent) design, because it seems to really work well so far.
For some methods, I might need to just return the HttpResponseMessage object and let the caller deal with it.
This is roughly what I have so far and I'm not sure how to make it compliant with the above OR whether I'm even doing this right. Any guidance is appreciated (flaming, however, is not).
// 200 (+User JSON) = success, otherwise APIError JSON
internal async Task<User> Login (string id, string password)
{
LoginPayload payload = new LoginPayload() { LoginId = id, Password = password};
var request = NewRequest(HttpMethod.Post, "login");
JsonPayload<LoginPayload>(payload, ref request);
return await Execute<Account>(request, false);
}
// 204: success, anything else failure
internal async Task<Boolean> LogOut ()
{
return await Execute<Boolean>(NewRequest(HttpMethod.Delete, "login"), true);
}
internal async Task<HttpResponseMessage> GetRawResponse ()
{
return await Execute<HttpResponseMessage>(NewRequest(HttpMethod.Get, "raw/something"), true);
}
internal async Task<Int32> GetMeStatusCode ()
{
return await Execute<Int32>(NewRequest(HttpMethod.Get, "some/intstatus"), true);
}
private async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate)
{
if (authenticate)
AuthenticateRequest(ref request); // add auth token to request
var tcs = new TaskCompletionSource<RESULT>();
var response = await client.SendAsync(request);
// TODO: If the RESULT is just HTTPResponseMessage, the rest is unnecessary
if (response.IsSuccessStatusCode)
{
try
{
// TryParse needs to handle Boolean differently than other types
RESULT result = await TryParse<RESULT>(response);
tcs.SetResult(result);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
else
{
try
{
APIError error = await TryParse<APIError>(response);
tcs.SetException(new APIException(error));
}
catch (Exception e)
{
tcs.SetException(new APIException("Unknown error"));
}
}
return tcs.Task.Result;
}
This is the APIError JSON structure (it's the status code + a custom error code).
{
"status": 404,
"code":216,
"msg":"User not found"
}
I would prefer to stay with System.Net, but that's mostly because I don't want to switch all my code over. If what I want is easier done in other ways then it's obviously worth the extra work.
Thanks.
Here is an example of how I've done it using MVC API 2 as backend. My backend returns a json result if the credentials are correct. UserCredentials class is the exact same model as the json result. You will have to use System.Net.Http.Formatting which can be found in the Microsoft.AspNet.WebApi.Client NugetPackage
public static async Task<UserCredentials> Login(string username, string password)
{
string baseAddress = "127.0.0.1/";
HttpClient client = new HttpClient();
var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("xyz:secretKey"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader);
var form = new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", username },
{ "password", password },
};
var Response = await client.PostAsync(baseAddress + "oauth/token", new FormUrlEncodedContent(form));
if (Response.StatusCode == HttpStatusCode.OK)
{
return await Response.Content.ReadAsAsync<UserCredentials>(new[] { new JsonMediaTypeFormatter() });
}
else
{
return null;
}
}
and you also need Newtonsoft.Json package.
public class UserCredentials
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
//more properties...
}
i would use a Deserializer.
HttpResponseMessage response = await client.GetAsync("your http here");
var responseString = await response.Content.ReadAsStringAsync();
[Your Class] object= JsonConvert.DeserializeObject<[Your Class]>(responseString.Body.ToString());
So, first to address the you need Newtonsoft.Json comments, I really haven't felt the need yet. I've found the built in support to work well so far (using the APIError Json in my original question:
[DataContract]
internal class APIError
{
[DataMember (Name = "status")]
public int StatusCode { get; set; }
[DataMember (Name = "code")]
public int ErrorCode { get; set; }
}
I have also defined a JsonHelper class to (de)serialize:
public class JsonHelper
{
public static T fromJson<T> (string json)
{
var bytes = Encoding.Unicode.GetBytes (json);
using (MemoryStream mst = new MemoryStream(bytes))
{
var serializer = new DataContractJsonSerializer (typeof (T));
return (T)serializer.ReadObject (mst);
}
}
public static string toJson (object instance)
{
using (MemoryStream mst = new MemoryStream())
{
var serializer = new DataContractJsonSerializer (instance.GetType());
serializer.WriteObject (mst, instance);
mst.Position = 0;
using (StreamReader r = new StreamReader(mst))
{
return r.ReadToEnd();
}
}
}
}
The above bits I already had working. As for a single method that would handle each request execution based on the type of result expected while it makes it easier to change how I handle things (like errors, etc), it also adds to the complexity and thus readability of my code. I ended up creating separate methods (all variants of the Execute method in the original question:
// execute and return response.StatusCode
private static async Task<HttpStatusCode> ExecuteForStatusCode (HttpRequestMessage request, bool authenticate = true)
// execute and return response without processing
private static async Task<HttpResponseMessage> ExecuteForRawResponse(HttpRequestMessage request, bool authenticate = true)
// execute and return response.IsSuccessStatusCode
private static async Task<Boolean> ExecuteForBoolean (HttpRequestMessage request, bool authenticate = true)
// execute and extract JSON payload from response content and convert to RESULT
private static async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate = true)
I can move the unauthorized responses (which my current code isn't handling right now anyway) into a new method CheckResponse that will (for example) log the user out if a 401 is received.

consuming a RESTFUL web service in c# with JSON [duplicate]

This is the code I have so far:
public class Class1
{
private const string URL = "https://sub.domain.com/objects.json?api_key=123";
private const string DATA = #"{""object"":{""name"":""Name""}}";
static void Main(string[] args)
{
Class1.CreateObject();
}
private static void CreateObject()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = DATA.Length;
StreamWriter requestWriter = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
requestWriter.Write(DATA);
requestWriter.Close();
try {
WebResponse webResponse = request.GetResponse();
Stream webStream = webResponse.GetResponseStream();
StreamReader responseReader = new StreamReader(webStream);
string response = responseReader.ReadToEnd();
Console.Out.WriteLine(response);
responseReader.Close();
} catch (Exception e) {
Console.Out.WriteLine("-----------------");
Console.Out.WriteLine(e.Message);
}
}
}
The problem is that I think the exception block is being triggered (because when I remove the try-catch, I get a server error (500) message. But I don't see the Console.Out lines I put in the catch block.
My Console:
The thread 'vshost.NotifyLoad' (0x1a20) has exited with code 0 (0x0).
The thread '<No Name>' (0x1988) has exited with code 0 (0x0).
The thread 'vshost.LoadReference' (0x1710) has exited with code 0 (0x0).
'ConsoleApplication1.vshost.exe' (Managed (v4.0.30319)): Loaded 'c:\users\l. preston sego iii\documents\visual studio 11\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe', Symbols loaded.
'ConsoleApplication1.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
A first chance exception of type 'System.Net.WebException' occurred in System.dll
The thread 'vshost.RunParkingWindow' (0x184c) has exited with code 0 (0x0).
The thread '<No Name>' (0x1810) has exited with code 0 (0x0).
The program '[2780] ConsoleApplication1.vshost.exe: Program Trace' has exited with code 0 (0x0).
The program '[2780] ConsoleApplication1.vshost.exe: Managed (v4.0.30319)' has exited with code 0 (0x0).
The ASP.NET Web API has replaced the WCF Web API previously mentioned.
I thought I'd post an updated answer since most of these responses are from early 2012, and this thread is one of the top results when doing a Google search for "call restful service C#".
Current guidance from Microsoft is to use the Microsoft ASP.NET Web API Client Libraries to consume a RESTful service. This is available as a NuGet package, Microsoft.AspNet.WebApi.Client. You will need to add this NuGet package to your solution.
Here's how your example would look when implemented using the ASP.NET Web API Client Library:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
namespace ConsoleProgram
{
public class DataObject
{
public string Name { get; set; }
}
public class Class1
{
private const string URL = "https://sub.domain.com/objects.json";
private string urlParameters = "?api_key=123";
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// List data response.
HttpResponseMessage response = client.GetAsync(urlParameters).Result; // Blocking call! Program will wait here until a response is received or a timeout occurs.
if (response.IsSuccessStatusCode)
{
// Parse the response body.
var dataObjects = response.Content.ReadAsAsync<IEnumerable<DataObject>>().Result; //Make sure to add a reference to System.Net.Http.Formatting.dll
foreach (var d in dataObjects)
{
Console.WriteLine("{0}", d.Name);
}
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
// Make any other calls using HttpClient here.
// Dispose once all HttpClient calls are complete. This is not necessary if the containing object will be disposed of; for example in this case the HttpClient instance will be disposed automatically when the application terminates so the following call is superfluous.
client.Dispose();
}
}
}
If you plan on making multiple requests, you should re-use your HttpClient instance. See this question and its answers for more details on why a using statement was not used on the HttpClient instance in this case: Do HttpClient and HttpClientHandler have to be disposed between requests?
For more details, including other examples, see Call a Web API From a .NET Client (C#)
This blog post may also be useful: Using HttpClient to Consume ASP.NET Web API REST Services
My suggestion would be to use RestSharp. You can make calls to REST services and have them cast into POCO objects with very little boilerplate code to actually have to parse through the response. This will not solve your particular error, but it answers your overall question of how to make calls to REST services. Having to change your code to use it should pay off in the ease of use and robustness moving forward. That is just my two cents though.
Example:
namespace RestSharpThingy
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using RestSharp;
public static class Program
{
public static void Main()
{
Uri baseUrl = new Uri("https://httpbin.org/");
IRestClient client = new RestClient(baseUrl);
IRestRequest request = new RestRequest("get", Method.GET) { Credentials = new NetworkCredential("testUser", "P455w0rd") };
request.AddHeader("Authorization", "Bearer qaPmk9Vw8o7r7UOiX-3b-8Z_6r3w0Iu2pecwJ3x7CngjPp2fN3c61Q_5VU3y0rc-vPpkTKuaOI2eRs3bMyA5ucKKzY1thMFoM0wjnReEYeMGyq3JfZ-OIko1if3NmIj79ZSpNotLL2734ts2jGBjw8-uUgKet7jQAaq-qf5aIDwzUo0bnGosEj_UkFxiJKXPPlF2L4iNJSlBqRYrhw08RK1SzB4tf18Airb80WVy1Kewx2NGq5zCC-SCzvJW-mlOtjIDBAQ5intqaRkwRaSyjJ_MagxJF_CLc4BNUYC3hC2ejQDoTE6HYMWMcg0mbyWghMFpOw3gqyfAGjr6LPJcIly__aJ5__iyt-BTkOnMpDAZLTjzx4qDHMPWeND-TlzKWXjVb5yMv5Q6Jg6UmETWbuxyTdvGTJFzanUg1HWzPr7gSs6GLEv9VDTMiC8a5sNcGyLcHBIJo8mErrZrIssHvbT8ZUPWtyJaujKvdgazqsrad9CO3iRsZWQJ3lpvdQwucCsyjoRVoj_mXYhz3JK3wfOjLff16Gy1NLbj4gmOhBBRb8rJnUXnP7rBHs00FAk59BIpKLIPIyMgYBApDCut8V55AgXtGs4MgFFiJKbuaKxq8cdMYEVBTzDJ-S1IR5d6eiTGusD5aFlUkAs9NV_nFw");
request.AddParameter("clientId", 123);
IRestResponse<RootObject> response = client.Execute<RootObject>(request);
if (response.IsSuccessful)
{
response.Data.Write();
}
else
{
Console.WriteLine(response.ErrorMessage);
}
Console.WriteLine();
string path = Assembly.GetExecutingAssembly().Location;
string name = Path.GetFileName(path);
request = new RestRequest("post", Method.POST);
request.AddFile(name, File.ReadAllBytes(path), name, "application/octet-stream");
response = client.Execute<RootObject>(request);
if (response.IsSuccessful)
{
response.Data.Write();
}
else
{
Console.WriteLine(response.ErrorMessage);
}
Console.ReadLine();
}
private static void Write(this RootObject rootObject)
{
Console.WriteLine("clientId: " + rootObject.args.clientId);
Console.WriteLine("Accept: " + rootObject.headers.Accept);
Console.WriteLine("AcceptEncoding: " + rootObject.headers.AcceptEncoding);
Console.WriteLine("AcceptLanguage: " + rootObject.headers.AcceptLanguage);
Console.WriteLine("Authorization: " + rootObject.headers.Authorization);
Console.WriteLine("Connection: " + rootObject.headers.Connection);
Console.WriteLine("Dnt: " + rootObject.headers.Dnt);
Console.WriteLine("Host: " + rootObject.headers.Host);
Console.WriteLine("Origin: " + rootObject.headers.Origin);
Console.WriteLine("Referer: " + rootObject.headers.Referer);
Console.WriteLine("UserAgent: " + rootObject.headers.UserAgent);
Console.WriteLine("origin: " + rootObject.origin);
Console.WriteLine("url: " + rootObject.url);
Console.WriteLine("data: " + rootObject.data);
Console.WriteLine("files: ");
foreach (KeyValuePair<string, string> kvp in rootObject.files ?? Enumerable.Empty<KeyValuePair<string, string>>())
{
Console.WriteLine("\t" + kvp.Key + ": " + kvp.Value);
}
}
}
public class Args
{
public string ClientId { get; set; }
}
public class Headers
{
public string Accept { get; set; }
public string AcceptEncoding { get; set; }
public string AcceptLanguage { get; set; }
public string Authorization { get; set; }
public string Connection { get; set; }
public string Dnt { get; set; }
public string Host { get; set; }
public string Origin { get; set; }
public string Referer { get; set; }
public string UserAgent { get; set; }
}
public class RootObject
{
public Args args { get; set; }
public Headers Headers { get; set; }
public string Origin { get; set; }
public string Url { get; set; }
public string Data { get; set; }
public Dictionary<string, string> Files { get; set; }
}
}
Unrelated, I'm sure, but do wrap your IDisposable objects in using blocks to ensure proper disposal:
using System;
using System.Net;
using System.IO;
namespace ConsoleProgram
{
public class Class1
{
private const string URL = "https://sub.domain.com/objects.json?api_key=123";
private const string DATA = #"{""object"":{""name"":""Name""}}";
static void Main(string[] args)
{
Class1.CreateObject();
}
private static void CreateObject()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = DATA.Length;
using (Stream webStream = request.GetRequestStream())
using (StreamWriter requestWriter = new StreamWriter(webStream, System.Text.Encoding.ASCII))
{
requestWriter.Write(DATA);
}
try
{
WebResponse webResponse = request.GetResponse();
using (Stream webStream = webResponse.GetResponseStream() ?? Stream.Null)
using (StreamReader responseReader = new StreamReader(webStream))
{
string response = responseReader.ReadToEnd();
Console.Out.WriteLine(response);
}
}
catch (Exception e)
{
Console.Out.WriteLine("-----------------");
Console.Out.WriteLine(e.Message);
}
}
}
}
Here are a few different ways of calling an external API in C# (updated 2019).
.NET's built-in ways:
WebRequest& WebClient - verbose APIs & Microsoft's documentation is not very easy to follow
HttpClient - .NET's newest kid on the block & much simpler to use than above.
Free, open-source NuGet Packages, which frankly have a much better developer experience than .NET's built in clients:
ServiceStack.Text (1,000 GitHub stars, 7 million NuGet downloads) (*) - fast, light and resilient.
RestSharp (6,000 GitHub stars, 23 million NuGet downloads) (*) - simple REST and HTTP API Client
Flurl (1,700 GitHub stars, 3 million NuGet downloads) (*)- a fluent, portable, testable HTTP client library
All the above packages provide a great developer experience (i.e., concise, easy API) and are well maintained.
(*) as at August 2019
Example: Getting a Todo item from a Fake Rest API using ServiceStack.Text.
The other libraries have very similar syntax.
class Program
{
static void Main(string[] args)
{
// Fake rest API
string url = "https://jsonplaceholder.typicode.com/todos/1";
// GET data from API & map to POCO
var todo = url.GetJsonFromUrl().FromJson<Todo>();
// Print the result to screen
todo.PrintDump();
}
public class Todo
{
public int UserId { get; set; }
public int Id { get; set; }
public string Title { get; set; }
public bool Completed { get; set; }
}
}
Running the above example in a .NET Core Console app, produces the following output.
Install these packages using NuGet
Install-Package ServiceStack.Text, or
Install-Package RestSharp, or
Install-Package Flurl.Http
A solution in ASP.NET Core:
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
namespace WebApp
{
public static class HttpHelper
{
// In my case this is https://localhost:44366/
private static readonly string apiBasicUri = ConfigurationManager.AppSettings["apiBasicUri"];
public static async Task Post<T>(string url, T contentValue)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(apiBasicUri);
var content = new StringContent(JsonConvert.SerializeObject(contentValue), Encoding.UTF8, "application/json");
var result = await client.PostAsync(url, content);
result.EnsureSuccessStatusCode();
}
}
public static async Task Put<T>(string url, T stringValue)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(apiBasicUri);
var content = new StringContent(JsonConvert.SerializeObject(stringValue), Encoding.UTF8, "application/json");
var result = await client.PutAsync(url, content);
result.EnsureSuccessStatusCode();
}
}
public static async Task<T> Get<T>(string url)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(apiBasicUri);
var result = await client.GetAsync(url);
result.EnsureSuccessStatusCode();
string resultContentString = await result.Content.ReadAsStringAsync();
T resultContent = JsonConvert.DeserializeObject<T>(resultContentString);
return resultContent;
}
}
public static async Task Delete(string url)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(apiBasicUri);
var result = await client.DeleteAsync(url);
result.EnsureSuccessStatusCode();
}
}
}
}
To post, use something like this:
await HttpHelper.Post<Setting>($"/api/values/{id}", setting);
Example for delete:
await HttpHelper.Delete($"/api/values/{id}");
Example to get a list:
List<ClaimTerm> claimTerms = await HttpHelper.Get<List<ClaimTerm>>("/api/values/");
Example to get only one:
ClaimTerm processedClaimImage = await HttpHelper.Get<ClaimTerm>($"/api/values/{id}");
Please use the below code for your REST API request:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Json;
namespace ConsoleApplication2
{
class Program
{
private const string URL = "https://XXXX/rest/api/2/component";
private const string DATA = #"{
""name"": ""Component 2"",
""description"": ""This is a JIRA component"",
""leadUserName"": ""xx"",
""assigneeType"": ""PROJECT_LEAD"",
""isAssigneeTypeValid"": false,
""project"": ""TP""}";
static void Main(string[] args)
{
AddComponent();
}
private static void AddComponent()
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new System.Uri(URL);
byte[] cred = UTF8Encoding.UTF8.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
System.Net.Http.HttpContent content = new StringContent(DATA, UTF8Encoding.UTF8, "application/json");
HttpResponseMessage messge = client.PostAsync(URL, content).Result;
string description = string.Empty;
if (messge.IsSuccessStatusCode)
{
string result = messge.Content.ReadAsStringAsync().Result;
description = result;
}
}
}
}
Calling a REST API when using .NET 4.5 or .NET Core
I would suggest DalSoft.RestClient (caveat: I created it). The reason being, because it uses dynamic typing, you can wrap everything up in one fluent call including serialization/de-serialization. Below is a working PUT example:
dynamic client = new RestClient("http://jsonplaceholder.typicode.com");
var post = new Post { title = "foo", body = "bar", userId = 10 };
var result = await client.Posts(1).Put(post);
GET:
// GET JSON Response
public WeatherResponseModel GET(string url) {
WeatherResponseModel model = new WeatherResponseModel();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
try {
WebResponse response = request.GetResponse();
using(Stream responseStream = response.GetResponseStream()) {
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
model = JsonConvert.DeserializeObject < WeatherResponseModel > (reader.ReadToEnd());
}
} catch (WebException ex) {
WebResponse errorResponse = ex.Response;
using(Stream responseStream = errorResponse.GetResponseStream()) {
StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
String errorText = reader.ReadToEnd();
// Log errorText
}
throw;
}
return model;
}
POST:
// POST a JSON string
void POST(string url, string jsonContent) {
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
Byte[]byteArray = encoding.GetBytes(jsonContent);
request.ContentLength = byteArray.Length;
request.ContentType = # "application/json";
using(Stream dataStream = request.GetRequestStream()) {
dataStream.Write(byteArray, 0, byteArray.Length);
}
long length = 0;
try {
using(HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
// Got response
length = response.ContentLength;
}
} catch (WebException ex) {
WebResponse errorResponse = ex.Response;
using(Stream responseStream = errorResponse.GetResponseStream()) {
StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
String errorText = reader.ReadToEnd();
// Log errorText
}
throw;
}
}
Note: To serialize and desirialze JSON, I used the Newtonsoft.Json NuGet package.
Check out Refit for making calls to REST services from .NET. I've found it very easy to use:
Refit: The automatic type-safe REST library for .NET Core, Xamarin and
.NET
Refit is a library heavily inspired by Square's Retrofit library, and
it turns your REST API into a live interface:
public interface IGitHubApi {
[Get("/users/{user}")]
Task<User> GetUser(string user);
}
// The RestService class generates an implementation of IGitHubApi
// that uses HttpClient to make its calls:
var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com");
var octocat = await gitHubApi.GetUser("octocat");
This is example code that works for sure. It took me a day to make this to read a set of objects from a REST service:
RootObject is the type of the object I'm reading from the REST service.
string url = #"http://restcountries.eu/rest/v1";
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(IEnumerable<RootObject>));
WebClient syncClient = new WebClient();
string content = syncClient.DownloadString(url);
using (MemoryStream memo = new MemoryStream(Encoding.Unicode.GetBytes(content)))
{
IEnumerable<RootObject> countries = (IEnumerable<RootObject>)serializer.ReadObject(memo);
}
Console.Read();
I did it in this simple way, with Web API 2.0. You can remove UseDefaultCredentials. I used it for my own use cases.
List<YourObject> listObjects = new List<YourObject>();
string response = "";
using (var client = new WebClient() { UseDefaultCredentials = true })
{
response = client.DownloadString(apiUrl);
}
listObjects = JsonConvert.DeserializeObject<List<YourObject>>(response);
return listObjects;
var TakingRequset = WebRequest.Create("http://xxx.acv.com/MethodName/Get");
TakingRequset.Method = "POST";
TakingRequset.ContentType = "text/xml;charset=utf-8";
TakingRequset.PreAuthenticate = true;
//---Serving Request path query
var PAQ = TakingRequset.RequestUri.PathAndQuery;
//---creating your xml as per the host reqirement
string xmlroot=#"<root><childnodes>passing parameters</childnodes></root>";
string xmlroot2=#"<root><childnodes>passing parameters</childnodes></root>";
//---Adding Headers as requested by host
xmlroot2 = (xmlroot2 + "XXX---");
//---Adding Headers Value as requested by host
// var RequestheaderVales = Method(xmlroot2);
WebProxy proxy = new WebProxy("XXXXX-----llll", 8080);
proxy.Credentials = new NetworkCredential("XXX---uuuu", "XXX----", "XXXX----");
System.Net.WebRequest.DefaultWebProxy = proxy;
// Adding The Request into Headers
TakingRequset.Headers.Add("xxx", "Any Request Variable ");
TakingRequset.Headers.Add("xxx", "Any Request Variable");
byte[] byteData = Encoding.UTF8.GetBytes(xmlroot);
TakingRequset.ContentLength = byteData.Length;
using (Stream postStream = TakingRequset.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
postStream.Close();
}
StreamReader stredr = new StreamReader(TakingRequset.GetResponse().GetResponseStream());
string response = stredr.ReadToEnd();
The answer marked here suggests using HttpClient directly and the disposing of it. This might work, but it's quite easy to run in to problems with HttpClient if you don't use it correctly.
If you're going to use HttpClient, you're better off handing over the creation/disposal of HttpClients to a third-party library that uses the factory pattern. RestClient.Net is one such library.
It comes with a very basic HttpClient factory so that you don't run in to the socket exhaustion problem,
public class DefaultHttpClientFactory : IHttpClientFactory, IDisposable
{
#region Fields
private bool disposed;
private readonly ConcurrentDictionary<string, Lazy<HttpClient>> _httpClients;
private readonly Func<string, Lazy<HttpClient>> _createClientFunc;
#endregion
#region Constructor
public DefaultHttpClientFactory() : this(null)
{
}
public DefaultHttpClientFactory(Func<string, Lazy<HttpClient>> createClientFunc)
{
_createClientFunc = createClientFunc;
_httpClients = new ConcurrentDictionary<string, Lazy<HttpClient>>();
if (_createClientFunc != null) return;
_createClientFunc = name =>
{
return new Lazy<HttpClient>(() => new HttpClient(), LazyThreadSafetyMode.ExecutionAndPublication);
};
}
#endregion
#region Implementation
public HttpClient CreateClient(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return _httpClients.GetOrAdd(name, _createClientFunc).Value;
}
public void Dispose()
{
if (disposed) return;
disposed = true;
foreach (var name in _httpClients.Keys)
{
_httpClients[name].Value.Dispose();
}
}
#endregion
}
But Microsoft's IHttpClientFactory implementation can also be used for the latest and greatest:
var serviceCollection = new ServiceCollection();
var baseUri = new Uri("http://www.test.com");
serviceCollection.AddSingleton(typeof(ISerializationAdapter), typeof(NewtonsoftSerializationAdapter));
serviceCollection.AddSingleton(typeof(ILogger), typeof(ConsoleLogger));
serviceCollection.AddSingleton(typeof(IClient), typeof(Client));
serviceCollection.AddDependencyInjectionMapping();
serviceCollection.AddTransient<TestHandler>();
//Make sure the HttpClient is named the same as the Rest Client
serviceCollection.AddSingleton<IClient>(x => new Client(name: clientName, httpClientFactory: x.GetRequiredService<IHttpClientFactory>()));
serviceCollection.AddHttpClient(clientName, (c) => { c.BaseAddress = baseUri; })
.AddHttpMessageHandler<TestHandler>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var client = serviceProvider.GetService<IClient>();
await client.GetAsync<object>();
RestClient.Net takes in to account dependency injection, mocking, IoC containers, unit testability, and above all is fast. I've hunted around and the only the other client that seems to work in a similar capacity is Flurl.Http.
We have started using speakeasy. It is great:
https://github.com/jonnii/SpeakEasy
// create a client
var client = HttpClient.Create("http://example.com/api");
// get some companies!
var companies = client.Get("companies").OnOk().As<List<Company>>();
// upload a company, with validation error support
client.Post(company, "companies")
.On(HttpStatusCode.BadRequest, (List<ValidationError> errors) => {
Console.WriteLine("Ruh Roh, you have {0} validation errors", errors.Count());
})
.On(HttpStatusCode.Created, () => Console.WriteLine("Holy moly you win!"));
// update a company
client.Put(company, "company/:id", new { id = "awesome-sauce" })
.OnOk(() => Console.WriteLine("Company updated"));
// run a search
client.Get("images/:category", new { category = "cats", breed = "omg the cutest", size = "kittens" })
.OnOk().As<List<Image>>();
// make an asynchronous request
var response = await client.GetAsync("companies/:id", new { id = 5 })
response.OnOk(UpdateCompaniesCallback)
Since you are using Visual Studio 11 Beta, you will want to use the latest and greatest. The new Web API contains classes for this.
See HttpClient: http://wcf.codeplex.com/wikipage?title=WCF%20HTTP
HTTP GET Request
string api = this.configuration["getApiUrl"];//Read from Iconfiguration object injected
public async Task<HttpResponseMessage> GetAsync(string api, ILogger log, params dynamic[] parameters)
{
log.LogInformation($"Get Token");
var token = await GetTokenAsync(this.configuration["ClientId"], this.configuration["AppKey"]).ConfigureAwait(false);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(BearerTokenName, token);
var apiBaseUrl = this.configuration["BaseUrl"];
client.BaseAddress = new Uri(apiBaseUrl);
var apiUrl = ConstructUrl(api, parameters);
var result = await client.GetAsync(apiUrl).ConfigureAwait(false);
if (result.StatusCode == System.Net.HttpStatusCode.OK)
{
return result;
}
else
{
throw new HttpResponseException(new HttpResponseMessage(result.StatusCode) { Content = new StringContent(result.ReasonPhrase) });
}
}
}
Read String from HttpResponseMessage as below
var client = await this.httpClientService.GetAsync(url, logger, Convert.ToInt32(Id, CultureInfo.InvariantCulture)).ConfigureAwait(false);
var response = await client.Content.ReadAsStringAsync();
HTTP POST Request
public async Task<string> PostAsync(string api, string contentValue, ILogger logger)
{
var token = await GetTokenAsync(this.configuration["ClientId"], this.configuration["AppKey"]).ConfigureAwait(false);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(BearerTokenName, token);
client.BaseAddress = new Uri(resource);
var content = new StringContent(contentValue, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await client.PostAsync(new Uri(api, UriKind.Relative), content).ConfigureAwait(false);
if (result.StatusCode == System.Net.HttpStatusCode.OK)
{
return await result.Content.ReadAsStringAsync();
}
else
{
throw new HttpResponseException(new HttpResponseMessage(result.StatusCode) { Content = new StringContent(result.ReasonPhrase) });
}
}
}
var response = await this.httpClientService.PostAsync(this.configuration["getDetails"], content, this.configuration["ApiBaseUrl"], logger).ConfigureAwait(false);
catch (System.Web.Http.HttpResponseException httpException)
{
if (httpException.Response.StatusCode == HttpStatusCode.Unauthorized)
{
logger.LogError($"Failed To Update", httpException);
}
else
{
throw;
}
}
return response;
The first step is to create the helper class for the HTTP client.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace callApi.Helpers
{
public class CallApi
{
private readonly Uri BaseUrlUri;
private HttpClient client = new HttpClient();
public CallApi(string baseUrl)
{
BaseUrlUri = new Uri(baseUrl);
client.BaseAddress = BaseUrlUri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public HttpClient getClient()
{
return client;
}
public HttpClient getClientWithBearer(string token)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return client;
}
}
}
Then you can use this class in your code.
This is an example of how you call the REST API without bearer using the above class.
// GET API/values
[HttpGet]
public async Task<ActionResult<string>> postNoBearerAsync(string email, string password,string baseUrl, string action)
{
var request = new LoginRequest
{
email = email,
password = password
};
var callApi = new CallApi(baseUrl);
var client = callApi.getClient();
HttpResponseMessage response = await client.PostAsJsonAsync(action, request);
if (response.IsSuccessStatusCode)
return Ok(await response.Content.ReadAsAsync<string>());
else
return NotFound();
}
This is an example of how you can call the REST API that require bearer.
// GET API/values
[HttpGet]
public async Task<ActionResult<string>> getUseBearerAsync(string token, string baseUrl, string action)
{
var callApi = new CallApi(baseUrl);
var client = callApi.getClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.GetAsync(action);
if (response.IsSuccessStatusCode)
{
return Ok(await response.Content.ReadAsStringAsync());
}
else
return NotFound();
}
You can also refer to the below repository if you want to see the working example of how it works.
https://github.com/mokh223/callApi

Categories

Resources