How to call a WebAPI from Windows Service - c#

I have an application written in Windows Service and this app need to make a call to a WebAPI written in Asp.Net MVC 4 WebAPi. this method in WebAPI return a DTO with primitive type, something like:
class ImportResultDTO {
public bool Success { get; set; }
public string[] Messages { get; set; }
}
and in my webapi
public ImportResultDTO Get(int clientId) {
// process.. and create the dto result.
return dto;
}
My question is, how can I call the webApi from the Windows Service? I have my URL and value of parameter, but I don't know how to call and how to deserialize the xml result to the DTO.
Thank you

You could use System.Net.Http.HttpClient. You will obviously need to edit the fake base address and request URI in the example below but this also shows a basic way to check the response status as well.
// Create an HttpClient instance
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:8888/");
// Usage
HttpResponseMessage response = client.GetAsync("api/importresults/1").Result;
if (response.IsSuccessStatusCode)
{
var dto = response.Content.ReadAsAsync<ImportResultDTO>().Result;
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}

You can install this NuGet package Microsoft ASP.NET Web API Client Libraries to your Windows Service project.
Here is a simple code snippet demonstrating how to use HttpClient:
var client = new HttpClient();
var response = client.GetAsync(uriOfYourService).Result;
var content = response.Content.ReadAsAsync<ImportResultDTO>().Result;
(I'm calling .Result() here for the sake of simplicity...)
For more sample of HttpClient, please check this out: List of ASP.NET Web API and HttpClient Samples.

Related

Console app not posting to minimalistic ASP.NET Core API

I have a solution with two projects in Visual Studio 2022:
A console app
A minimalistic API created using ASP.NET Core Web API
The API has a simplistic model as follows:
class Todo // This is the model
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
Which ought to represent Todo items in a small database, similar to the tutorial shown in the ASP.NET docs. The API has POST, PUT and DELETE methods.
The POST method looks like this:
// POST
app.MapPost("/todoitems", (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
db.SaveChanges();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
I decided to make synchronous calls to simplify this a little bit.
The console application attempts to make use of the POST method exposed by the API to add a Todo item in the database. Here's what I was able to do so far, thanks to the REST client tutorial in the Microsoft docs:
using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Json;
namespace InteractWithTodoApi
{
class Program
{
private static readonly HttpClient client = new HttpClient();
private static string repoLink = "https://localhost:7157/todoitems";
private static async Task PostData()
{
JsonContent content = JsonContent.Create("{\"id\": 1, \"Name\": \"Wash dishes\", \"IsComplete\": true}", typeof(string));
HttpResponseMessage postTask = await client.PostAsync(repoLink, content);
Console.WriteLine(postTask.Content);
}
static async Task Main(string[] args)
{
await PostData();
}
}
}
I then launch the API with a localhost, and once it is up and running I execute the console app. Then I refresh the API in my browser under https://localhost:PORT/todoitems.
After I refresh my browser, I expect to see a new entry
corresponding to:
{ "Id": 1, "Name": "Wash dishes", "IsComplete": true }"
The console app doesn't crash and runs to completion, however when I refresh that browser, I see that my Todo list is still empty: [].
What am I doing wrong here?
It seems that specifying the header is important while interacting with a JSON-based API. There appear to be two ways of doing this while working with System.Net.Http
Set globally using something like:
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
Set for each POST request as follows:
string JsonString = "{\"name\": \"Wash dishes\", \"isComplete\": true}";
StringContent content = new StringContent(JsonString, Encoding.UTF8, "application/json");
The default is not application/json in System.Net.Http. It appears to be some other kind of text-based value.

Splunk REST API Post route is throwing Cannot perform action "POST" without a target name to act on .net core

I am trying to create a role using splunk REST API (https://docs.splunk.com/Documentation/Splunk/8.2.5/RESTREF/RESTaccess#authorization.2Froles). The route works perfectly using postman, but when I try to invoke the same route using IServiceClient via .net core, I run in to "Cannot perform action "POST" without a target name to act on". Please let me know what other information I can provide. Have been searching on google for the error and in splunk documentation with no luck.
using (var client = _serviceClientFactory.GetServiceClient()) //client service factory returns a ServiceClient
{
PostRoleRequest request = new PostRoleRequest
{
name = Const.DefaultRoleName //this is the only required param
};
var response = await client.PostAsync<PostRoleResponse>($"/services/authorization/roles?output_mode=json", request);
}
public class PostRoleRequest
{
public string name { get; set; }
}

ServiceStack Performance

Let me start by saying I love the design of ServiceStack as a client. (I've never used it for server side)
I'm writing a C# wrapper for API calls and I keep getting timeout and authentication errors. I've contacted the developers at the other end and they assure me that there are no issues on their end and that I must be doing something wrong. Normally I wouldn't believe them and I'd build a sample project to demonstrate the issue but in this case they pointed me to a web page that will test the same API I'm running in C# and they can re-authenticate as fast as they can click the submit button. I forget the exact site they use for testing but enough of my story... I'm sure I'm doing something wrong I just don't know what.
Here's my Unit Test. If I run it by itself or with one copy it works fine (150-1100ms) but if I make 3 or more copies of it they I will get only 2-3 that pass and the rest will timeout.
[TestMethod]
[Timeout(5000)]
public void Login_Success1()
{
var client = new JsonServiceClient("apiurl");
var response = client.Login("XXXAccessKeyXXX", "XXXSecretKeyXXX");
//Assertions
}
This is my extension method:
public static class Extensions
{
public static (bool Success, string Message, string Token) Login(this JsonServiceClient client, string accessKey, string secretKey)
{
try
{
var response = client.Post(new LoginRequest(accessKey, secretKey));
var authorization = response.Headers.GetValues("Authorization")[0];
return (true, string.Empty, authorization);
}
catch (Exception ex)
{
return (false, $"Authentication failed: {ex.Message}", string.Empty);
}
}
}
And here's the login request:
[Route("/sessions")]
[DataContract]
internal class LoginRequest
{
internal LoginRequest(string accessKey, string secretKey)
{
AccessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey));
SecretKey = secretKey ?? throw new ArgumentNullException(nameof(secretKey));
}
[DataMember(Name = "accessKey")]
internal string AccessKey { get; set; }
[DataMember(Name = "secretKey")]
internal string SecretKey { get; set; }
}
I think this is all the relevant code but if you feel I missed something please lmk.
Your Request DTO's should implement either IReturn<T> or IReturnVoid otherwise if you're sending just an object you will call the deprecated Post() method:
/// <summary>
/// APIs returning HttpWebResponse must be explicitly Disposed, e.g using (var res = client.Post(url)) { ... }
/// </summary>
[Obsolete("Use: using (client.Post<HttpWebResponse>(requestDto) { }")]
public virtual HttpWebResponse Post(object requestDto)
{
return Send<HttpWebResponse>(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto);
}
Which because ServiceStack doesn't know how you want the Response deserialized it will return the open HttpWebResponse so you can inspect the Response yourself (as you're doing in your example). But this needs to be explicitly disposed as .NET's HttpWebRequest only allows a couple of concurrent requests open per domain which will cause your App to hang/timeout as it's waiting for Requests to be disposed to stay within the concurrent limit.
The preferred solution is to always annotate Request DTO's that you send with ServiceStack clients with a IReturn or a IReturn<T> interface marker, if it has none or you want to ignore the Response implement IReturnVoid otherwise implement IReturn<ResponseDtoType>:
class LoginRequest : IReturnVoid {}
Which instead calls the non-deprecated Post() method which disposes of the HttpWebResponse.
Otherwise if you want to send plain object DTO's you need to dispose of the HttpWebResponse after usage, e.g:
using (var response = client.Post<HttpWebResponse>(new LoginRequest(accessKey, secretKey)))
{
var authorization = response.Headers.GetValues("Authorization")[0];
}
API's which implicitly return HttpWebResponse were deprecated to avoid hard to identify issues like this, instead we recommend using the explicit API above which declares the HttpWebResponse return type at the call-site so it's easier to identify it needs to be disposed.
Also note the ServiceStack Service Clients are opinionated for calling ServiceStack Services, for calling other Services we recommend using HTTP Utils instead.

Singleton HttpClient calling GetAsync/SendAsync with async/await never returns

I am building an application using Xamarin (Android), it uses a PCL project as a Service layer. I have a Web Api endpoint and I am using HttpClient to consume it.
Everything works fine, but if I leave my Android app open and idle for a while (like 2 minutes) and I try to make a new request, the first request using the singleton HttpClient won't work. It just never returns and stays there until it timeouts (TaskCancelledException). I also put a breakpoint on my Api and it doesn't get hit. If I try to send the request again, then it works.
After a lot of debugging I found that this only happens if I try to use the HttpClient as a Singleton. If I create a new HttpClient for every request everything works.
At first I thought this was a deadlock issue, I've done a lot of research and double checked everything following the guidelines described in this other answer and Stephen Cleary's excellent post and I'm almost sure this is not the case.
I'm using ConfigureAwait(false) in every call from my PCL project so it doesn't capture the context.
The flow of a request goes like this:
Inside an Android Fragment:
SampleService svc = new SampleService();
response = await svc.GetAllSamples();
The service called (in my PCL project):
public class SampleService
{
public HttpClient Client { get; set; }
public SampleService()
{
// resolves my singleton instance and uses my custom DelegatingHandler
Client = CustomHttpClient.Instance;
}
public async Task<IEnumerable<Sample>> GetAllSamples()
{
IEnumerable<Sample> list = null;
// this never returns and timeouts the first time
using (var response = await Client.GetAsync("samples").ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
lista = await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Sample>>(json)).ConfigureAwait(false);
}
return list;
}
}
}
This is how I build my Singleton instance:
public sealed class CustomHttpClient
{
private static HttpClient _client;
public static HttpClient GetClient()
{
if (_client == null)
{
HttpMessageHandler messageHandler = new HttpClientHandler();
_client = new HttpClient(messageHandler);
_client.Timeout = TimeSpan.FromSeconds(30);
_client.BaseAddress = new Uri("myendpoint");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
return _client;
}
}
I tried to simplify and isolate the code here, if I can provide any other useful snippets, just let me know.
Am I doing something wrong regarding singletons HttpClient that I'm not aware of?
Update: Just for clarification, I'm trying to use HttpClient as a Singleton just because, as I found in this answer by Darrel Miller and in the book Designing Evolvable Web APIs with ASP.NET (Chapter 14), they were designed to be reusable and thread-safe (in most of the cases). From my research I'm not using anything that is not thread-safe in it.

Consuming WebAPI JSON

I'm trying to build some kind of RESTful-like API, I'm aware of that my first draft probably isn't anything near the real RESTful design pattern. However my real question is how should I consume my service using JSON?
In my so called real world example I want my users to sign in via the service so I have this AuthenticationController
namespace RESTfulService.Controllers
{
public class AuthenticationController : ApiController
{
public string Get(string username, string password)
{
// return JSON-object or JSON-status message
return "";
}
public string Get()
{
return "";
}
}
}
Considering the increasing popularity with the technology I assumed that very little code would be needed for consuming the service. Do I really need to serialize the JSON manually with some kind of third party package like json.net? Beneath is my draft for the client
private static bool DoAuthentication(string username, string password)
{
var client = InitializeHttpClient();
HttpResponseMessage response = client.GetAsync("/api/rest/authentication").Result;
if (response.IsSuccessStatusCode)
{
//retrieve JSON-object or JSON-status message
}
else
{
// Error
}
return true;
}
private static HttpClient InitializeHttpClient()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
How do I send JSON from service and how do I interpreting it on the client?
Have a look at the System.Net.Http.HttpContentExtensions in System.Net.Http.Formatting.dll. As explained here (and suggested by Mike Wasson in a comment above), you can call ReadAsAsync<T>() on the response content to deserialize from JSON (or XML) to a CLR type:
if (response.IsSuccessStatusCode)
{
var myObject = response.Content.ReadAsAsync<MyObject>();
}
If you need to customize the deserialization, that article links to a further explanation of MediaTypeFormatters.

Categories