I have a method that I created for simplifying using HttpClient calls. it uses the method HttpReponse.Content.ReadAsAsync().Result to get the response from the API.
This all works fine. My method looks something like this:
public static T ExecuteAPIGetRequest<T>(string url, Dictionary<string, string> parameters)
{
HttpClient client = new HttpClient();
//basic authentication
var t = new object();
string baseURL = "myurl";
//Execute request
HttpResponseMessage response = client.GetAsync(baseURL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return (T)t;
}
}
My question is, if the query fails it needs to return an empty type of T. This is fine if its a custom class I have written, but it does not work for objects like string or string[]. Any ideas?
Cheers
NCBL
try to return default(T)
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return default(T);
}
default will return null for reference types, and zeros numeric values int, double, etc.. and corresponding default values for custom struct and enum.
Daniel kindly noted one issue: if for reference types you want to return default object and not null, you should define generic constraint new T(). Now you can instantiate object of type T using call to parameter-less constructor. Full method is given below:
public static T ExecuteAPIGetRequest<T>(string url,
Dictionary<string, string> parameters)
where T : new()
{
HttpClient client = new HttpClient();
//basic authentication
string baseURL = "myurl";
HttpResponseMessage response = client.GetAsync(baseURL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return new T(); //returns an instance, not null
}
}
Now you will return default object for reference types, not null. Open type T can only take types, which have constructor by default (without parameters)
Can I make a suggestion that you consider an approach like this....
class Program
{
static void Main(string[] args)
{
var client = new HttpClient();
//basic authentication
string baseURL = "myurl";
var link = Link<Foo>.Create(baseURL);
var response = client.SendAsync(link.CreateRequest()).Result;
var myfoo = link.ProcessResponse(response);
}
}
public class Link<T>
{
public Uri Target { get; set; }
public static Link<T> Create(string url)
{
return new Link<T>() {Target = new Uri(url)};
}
public HttpRequestMessage CreateRequest()
{
return new HttpRequestMessage() {RequestUri = Target};
}
public T ProcessResponse(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<T>().Result;
}
else
{
return new T(); //returns an instance, not null
}
}
}
public class Foo
{
}
By encapsulating the mechanics of creating the link into a static factory method and the handling of the response into the ProcessResponse method you get a similar level of re-usability, but you also get the benefits of reusing the same HttpClient. This allows you to actually take advantage of the DefaultRequestHeaders and it will stop HttpClient from keep closing the connection when it gets disposed.
Also, by avoiding wrapping the Async call inside a sync method, you can let the calling code decide whether to block for the result or handle it asynchronously. You are likely to run into deadlock issues at some point with the way you are currently using .Result.
This technique of creating a Link class to encapsulate specific behaviour relating to de-referencing a URL can be extremely useful. It is easy to add parameters that can be used to fill in URI templates. You can also use it to handle request bodies.
Related
Here is my code:
public class Uploader
{
private readonly HttpMessageHandler m_httpMessageHandler;
public Uploader(HttpMessageHandler httpMessageHandler)
{
m_httpMessageHandler = httpMessageHandler;
}
public async Task<string> Upload(string url, Dictionary<string, string> data)
{
HttpResponseMessage result;
try
{
if (somecheck)
{
HttpClientHandler handler = (HttpClientHandler)m_httpMessageHandler;
// error occurs here
handler.ServerCertificateCustomValidationCallback = delegate { return true; };
}
var client = new HttpClient(m_httpMessageHandler);
result = await client.PostAsync(url, new FormUrlEncodedContent(data));
if (result.StatusCode != HttpStatusCode.OK)
{
return Strings.LabelPrinterNotConfiguredError;
}
else
{
return null; // Success!
}
}
catch(Exception e)
{
// do some stuff
}
}
}
In my Startup.cs
services.AddSingleton<HttpMessageHandler, HttpClientHandler>();
The reason why I have this is because I've created a mock HttpClientHandler used for unit testing the HttpClient. This way I can extend from the abstract class HttpMessageHandler for the mock implementation in my tests.
I am getting this error:
System.InvalidOperationException:
This instance has already started one or more requests. Properties can
only be modified before sending the first request.
After reading through several other answers on SO, I know that the issue is that the single HttpClientHandler that I have is somehow having its properties modified after starting which the runtime does not like. However, I still can't figure out why that is happening because it does not appear that I am overtly modifying BaseAddress, Timeout, or MaxResponseContentBufferSize.
The answers appear to be to modify the HttpClientHandler after creating it but if so, how do I set the ServerCertificateCustomValidationCallback member variable appropriately?
The follow-up question would be: how would I fix this error while also keeping the ability to mock/test HttpClient?
I am writing a client library for an API that provides both public and authenticated endpoints. I would like to easily denote which endpoints require authentication using attributes. For instance:
public async Task<ApiResponse> GetPublicData()
{
var request = CreateRequest( "v1/public" );
return await _httpClient.GetAsync( request );
}
[RequiresAuthentication]
public async Task<ApiResponse> GetPrivateData()
{
var request = CreateRequest( "v1/private" );
return await _httpClient.GetAsync( request );
}
private ApiRequest CreateRequest( string endpoint )
{
var request = new ApiRequest( endpoint );
// if (caller has RequiresAuthenticationAttribute)
// SignRequest( request, _credentials );
return request;
}
As far as I am aware, the only way I can access whether or not the caller of CreateRequest has a RequiresAuthenticationAttribute is to create a stack frame, find the method via reflection, and then attempt to get the attribute from the MethodInfo. This can be incredibly slow.
Is there any other way to pass this information into the callee that won't kill performance. I know that the requests will always be limited by the response time of the API, but given that it has to do with financial data, being able to fire off requests as soon as possible is a must, and being able to do it in a clean way that involves attributes instead of manually passing parameters would be very nice.
You could try using the CallerMemberNameAttribute class.
The attributes "Allows you to obtain the method or property name of the caller to the method."
private ApiRequest CreateRequest(string endpoint, [CallerMemberName] string callerMemberName= "")
{
var methodInfo = this.GetType().GetMethod(callerMemberName);
var attributes = (RequiresAuthenticationAttribute)method.GetCustomAttributes(typeof(RequiresAuthenticationAttribute), true);
var request = new ApiRequest( endpoint );
if (attributes.Any())
SignRequest(request, _credentials);
return request;
}
If you are set on using attributes, then you are going to have to use Reflection in one way or another. Some reflection mechanisms are faster than others, but there is still a runtime penalty which you will have to pay. On the other hand, if what you want is a separation of concerns (and using attributes is not a given), then you might want to think about using interfaces to separate those concerns.
For example:
public interface IAuthenticated
{
public async Task<ApiResponse> GetPrivateData();
}
public interface IPublicAccess
{
public async Task<ApiResponse> GetPublicData();
}
public async Task<ApiResponse> IPublicAccess.GetPublicData()
{
var request = CreateRequest( "v1/public" );
return await _httpClient.GetAsync( request );
}
public async Task<ApiResponse> IAuthenticated.GetPrivateData()
{
var request = CreateRequest( "v1/private" );
return await _httpClient.GetAsync( request );
}
private ApiRequest CreateRequest( string endpoint )
{
var request = new ApiRequest( endpoint );
// if (caller has RequiresAuthenticationAttribute)
// SignRequest( request, _credentials );
return request;
}
I have a simple controller method like this:
public IEnumerable<IEntity> GetEntities(ParamsModel args)
{
//set break point here to examine the args
return null;
}
And here is my ParamsModel:
public class ParamsModel {
public string Test;
}
And here is my client's method to send get request:
//_client here is an instance of RestClient
public async Task<IEnumerable<T>> GetEntitiesAsync()
{
var request = new RestRequest("somePath");
var o = new {
Test = "OK"
};
request.AddJsonBody(o);
return await _client.GetAsync<List<T>>(request);
}
After running the method GetEntitiesAsync, the break point (in the controller's method) is hit. However the args is null, really?
I've also tried the following:
public async Task<IEnumerable<T>> GetEntitiesAsync()
{
var request = new RestRequest("somePath");
request.AddParameter("Test", "OK");
return await _client.GetAsync<List<T>>(request);
}
However that did not work as well (args is null in the controller's method).
If I change the controller's method to something like this (and use the client code as right above), I can see the single simple argument of string has value parsed OK ("OK") inside the controller's method:
public IEnumerable<IEntity> GetEntities(string Test)
{
//here we can see that Test has value of "OK"
return null;
}
Really I don't understand what's wrong with my code.
Actually I worked with RestSharp at least a year ago but now it seems to have some new methods (such as the GetAsync as I used in my code), as before I used the Execute and ExecuteAsync.
Could you spot anything wrong here? Thanks!
PS: I'm using RestSharp 106.6.7
Update action to state explicitly where to look for and bind data using [FromUri]
public IHttpActionResult GetEntities([FromUri]ParamsModel args) {
//...
return Ok(entities);
}
To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter.
Reference Parameter Binding in ASP.NET Web API
The example with AddParameter
public async Task<IEnumerable<T>> GetEntitiesAsync() {
var request = new RestRequest("somePath");
request.AddParameter("Test", "OK");
return await _client.GetAsync<List<T>>(request);
}
Should work now.
Note that the model should use properties instead of fields
public class ParamsModel {
public string Test { get; set; }
}
This may not be possible.
Here is working code:
HttpResponseMessage playerResponse = await client.GetAsync("2018/export?TYPE=players&DETAILS=1&SINCE=&PLAYERS=9988%2C13604&JSON=1");
if (playerResponse.IsSuccessStatusCode)
{
var json = await playerResponse.Content.ReadAsStringAsync();
var o = JsonConvert.DeserializeObject<MFLPlayerAPIResult>(json);
playerList = o.PlayerData.MflPlayers.ToList();
}
The problem is I have lots of different models similar to MFLPlayerAPIResult like my code in the above, they all use different request strings. I am trying to build a function like this:
private async Task<Object> CreateListFromJson(Type jsonclass, string request, HttpClient client)
{
HttpResponseMessage response = await client.GetAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var o = JsonConvert.DeserializeObject<jsonclass>(json);
return (o);
}
return (null);
}
where the Object returned would be the same model as the Type jsonclass used in the parameters, then I could use the function like this:
playerList = CreateListFromJson(MFLPlayerAPIResult, request, client).PlayerData.MflPlayers.ToList();
And use it multiple times over and over with different request strings and model types.
Is this possible?
If you know the type ahead of time you can just take in a generic parameter like so:
private async Task<T> CreateListFromJson<T>(string request, HttpClient client)
where T: class
{
var myObj = JsonConvert.DeserializeObject<T>(item);
//...
}
If you don't know the type until compile time you can call the deserialize at runtime by passing the type
private async Task<Object> CreateListFromJson(Type jsonclass, string request, HttpClient client)
{
var myObj = JsonConvert.DeserializeObject(item, jsonclass);
//...
}
I've got my testing target class:
public class ApiClient
{
private IRestClient authz_rest_client;
private IRestClient api_rest_client;
// Injection feature for testing
internal ApiClient(IRestClient authz_rest_client, IRestClient api_rest_client)
{
this.authz_rest_client = authz_rest_client;
this.api_rest_client = api_rest_client;
}
//...
So, I inject my substituted RestSharp Clients as follows:
[TestFixture]
class AuthzApiClientTests
{
private ApiClient api_client;
private IRestClient authz_rest_client;
private IRestClient api_rest_client;
private IRestRequest request;
private IRestResponse<OAuth2AuthzCodeResponse> response;
[SetUp]
public void SetUp()
{
this.authz_rest_client = NSubstitute.Substitute.For<IRestClient>();
this.api_rest_client = NSubstitute.Substitute.For<IRestClient>();
this.request = NSubstitute.Substitute.For<IRestRequest>();
this.response = NSubstitute.Substitute.For<IRestResponse<OAuth2AuthzCodeResponse>>();
this.authz_rest_client.Execute<OAuth2AuthzCodeResponse>(request).Returns(response);
this.api_client = new ApiClient(this.authz_rest_client, this.api_rest_client);
this.api_client.configure(
"client_id",
"user",
"passwd"
);
}
Then, I write a test:
[Test]
public void Should_ReturnCorrectRequestTokenServiceEndpoint()
{
response.StatusCode = HttpStatusCode.Unauthorized;
response.Data = new OAuth2AuthzCodeResponse()
{
Error = StringEnum.GetStringValue(OAuth2ErrorTypes.invalid_client) //CLIENT IS NOT REGISTERED ON LEST SYSTEM.
};
this.api_client.Invoking(c => c.GrantAuthorization())
.ShouldThrow<OAuth2APIException>();
}
As you can see, I want to test my GrantAuthorization method of my ApiClient class. This method is:
IRestRequest authzcode_request = new AuthzCodeRequest(
this.settings.AuthzAuthorizeEndpoint,
this.settings.ClientId,
this.settings.ClientSecret,
this.settings.User,
this.settings.Password
);
IRestResponse<OAuth2AuthzCodeResponse> authzcode_response = this.authz_rest_client.Execute<OAuth2AuthzCodeResponse>(authzcode_request);
this.check_response(authzcode_response);
this.settings.AuthzCode = authzcode_response.Data.Code;
this.settings.AuthzCodeExpirationThreshold = DateTime.Now.AddSeconds(authzcode_response.Data.Expires_in);
The target of my test is "capture" my Execute<OAuth2AuthzCodeResponse> method in order to return my substituted response.
The problem is that, when I perform the test and I stop on this line, the result is not the response I've set previously.
I'm having trouble following through the full example, but I did notice something about the SetUp being using:
request = Substitute.For<IRestRequest>();
response = Substitute.For<IRestResponse<OAuth2AuthzCodeResponse>>();
authz_rest_client.Execute<OAuth2AuthzCodeResponse>(request).Returns(response);
The third line here says that whenever authz_rest_client.Execute<OAuth2AuthzCodeResponse>() is called with the request instance, it will return response. But request never seems to be used anywhere within the code, so Execute will never actually return that response.
Something like the following line will return response for any call to Execute<OAuth2AuthzCodeResponse>():
authz_rest_client.Execute<OAuth2AuthzCodeResponse>(null).ReturnsForAnyArgs(response);
But I'm not sure that's what you want either? Instead you might want to stub out individual calls, such as a specific response when it gets a request of type AuthzCodeRequest.
Solved!
I tried it using that:
this.authz_rest_client.Execute<OAuth2AuthzCodeResponse>(Arg.Any<IRestRequest>()).Returns(response);
This solution is similar to the pervious answer approach.
However, I not understand why not work perviouly...