I need to do a PATCH request with the Windows.Web.Http.HttpClient class and there is no official documentation on how to do it. How can I do this?
I found how to do a "custom" PATCH request with the previous System.Net.Http.HttpClient class here, and then fiddled with until I made it work in the Windows.Web.Http.HttpClient class, like so:
public async Task<HttpResponseMessage> PatchAsync(HttpClient client, Uri requestUri, IHttpContent iContent) {
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri) {
Content = iContent
};
HttpResponseMessage response = new HttpResponseMessage();
// In case you want to set a timeout
//CancellationToken cancellationToken = new CancellationTokenSource(60).Token;
try {
response = await client.SendRequestAsync(request);
// If you want to use the timeout you set
//response = await client.SendRequestAsync(request).AsTask(cancellationToken);
} catch(TaskCanceledException e) {
Debug.WriteLine("ERROR: " + e.ToString());
}
return response;
}
Update: See SSX-SL33PY's answer below for an even better solution, that does the same thing.
You can write the very same method as extension method, so you can invoke it directly on the HttpClient object:
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent iContent)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, requestUri)
{
Content = iContent
};
HttpResponseMessage response = new HttpResponseMessage();
try
{
response = await client.SendAsync(request);
}
catch (TaskCanceledException e)
{
Debug.WriteLine("ERROR: " + e.ToString());
}
return response;
}
}
Usage:
var responseMessage = await httpClient.PatchAsync(new Uri("testUri"), httpContent);
I'd like to extend on #alexander-pacha's answer and suggest adding following extension class somewhere in a common library. Wether this be a common library for a project / client / framework/... is something you'll have to make out on your own.
public static class HttpClientExtensions
{
/// <summary>
/// Send a PATCH request to the specified Uri as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, string requestUri, HttpContent content)
{
return client.PatchAsync(CreateUri(requestUri), content);
}
/// <summary>
/// Send a PATCH request to the specified Uri as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent content)
{
return client.PatchAsync(requestUri, content, CancellationToken.None);
}
/// <summary>
/// Send a PATCH request with a cancellation token as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, string requestUri, HttpContent content, CancellationToken cancellationToken)
{
return client.PatchAsync(CreateUri(requestUri), content, cancellationToken);
}
/// <summary>
/// Send a PATCH request with a cancellation token as an asynchronous operation.
/// </summary>
///
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1"/>.The task object representing the asynchronous operation.
/// </returns>
/// <param name="client">The instantiated Http Client <see cref="HttpClient"/></param>
/// <param name="requestUri">The Uri the request is sent to.</param>
/// <param name="content">The HTTP request content sent to the server.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="client"/> was null.</exception>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestUri"/> was null.</exception>
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri requestUri, HttpContent content, CancellationToken cancellationToken)
{
return client.SendAsync(new HttpRequestMessage(new HttpMethod("PATCH"), requestUri)
{
Content = content
}, cancellationToken);
}
private static Uri CreateUri(string uri)
{
return string.IsNullOrEmpty(uri) ? null : new Uri(uri, UriKind.RelativeOrAbsolute);
}
}
This way you're not awaiting and holding up execution in some static extension class, but you handle that as if you were really doing a PostAsync or a PutAsync call. You also have the same overloads at your disposal and you're letting the HttpClient handle everything it was designed to handle.
For it to work you need to pass the content like that:
HttpContent httpContent = new StringContent("Your JSON-String", Encoding.UTF8, "application/json-patch+json");
Step 1: Create a Static class (I have created as Extention)
public static class Extention
{
public static Task<HttpResponseMessage> PatchAsJsonAsync<T>(this HttpClient
client, string requestUri, T value)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter());
var request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri)
{ Content = content };
return client.SendAsync(request);
}
}
Step 2 : Call this method in your api request
private static HttpClient client = new HttpClient();
var response = Extention.PatchAsJsonAsync<UserUpdateAPIModel>(client, "https://api.go1.com/v2/users/5886043", data);
Problem solved , here if it is common url , then you can do it with your practice
Related
I am trying to start my own C# service as a Windows service. I installed my service using sc.exe create. I can see and modify the Service in the Services Application, but when I try to start, I am always getting the Error 1053.
In my Program I am starting a new Thread which runs an infinite while loop in Execute.
static void Main(string[] args) {
Console.WriteLine("Starting Thread");
mainThread = new Thread(new ThreadStart(Execute));
mainThread.Start();
Console.WriteLine("Thread Started!");
}
public static void Execute() {
//connect to Database
while(true) { //while connection open
...
}
}
When I run my Program manually, in a Console, Powershell and in Visual Studio, the Program runs as expected. When I try to run it via Services I get the error. I'm also getting the error when I'm running a empty Main (Main only with Console.WriteLine).
I googled really much and tried extending the Timeout in Registry(ServicesPipeTimeout), Using Threads, Installing Core Framework, Reinstalling the service and being owner of the service.
EDIT: I changed everything to run as a Hosted Service. In my Main i have
await new HostBuilder()
.ConfigureServices((hostContext, services) => { services.AddHostedService<Worker>(); })
.Build()
.RunAsync();
But i Still can't run it as a Windows Service, I'm getting the same Error.
I am out of Ideas, any help is highly appreciated.
Regards
What you wrote is a standard Console app. A service shall expose Start and Stop facilities to be correctly interpreted by Windows (or by SystemD equivalently).
Your core loop of the main should be hosted on a IHostedService, or a Worker service.
Give a look at [here][1] to figure out what you have to do.
I give you here below a plausible Program.cs file that I wrote some time ago for a Linux service (there is actually no difference with Windows services, just remove the .UseSystemD() call).
/// <summary>
/// Remote service App for Monday Parser and Database manager.
/// </summary>
public static class Program
{
private static readonly ILogger logger = new LoggerConfiguration()
.ReadFrom.Configuration(MondayConfiguration.Configuration, sectionName: "AppLog")
.CreateLogger().ForContext("Origin", "MondayService");
/// <summary>
/// Helper to create hosting environment.
/// </summary>
/// <param name="args">
/// command line arguments if any- no management is occurring now.
/// </param>
/// <returns>
/// </returns>
public static IHostBuilder CreateWebHostBuilder(string[] args)
{
string curDir = MondayConfiguration.DefineCurrentDir();
IConfigurationRoot config = new ConfigurationBuilder()
// .SetBasePath(Directory.GetCurrentDirectory())
.SetBasePath(curDir)
.AddJsonFile("servicelocationoptions.json", optional: false, reloadOnChange: true)
#if DEBUG
.AddJsonFile("appSettings.Debug.json")
#else
.AddJsonFile("appSettings.json")
#endif
.Build();
return Host.CreateDefaultBuilder(args)
.UseContentRoot(curDir)
.ConfigureAppConfiguration((_, configuration) =>
{
configuration
.AddIniFile("appSettings.ini", optional: true, reloadOnChange: true)
#if DEBUG
.AddJsonFile("appSettings.Debug.json")
#else
.AddJsonFile("appSettings.json")
#endif
.AddJsonFile("servicelocationoptions.json", optional: false, reloadOnChange: true);
})
.UseSerilog((_, services, configuration) => configuration
.ReadFrom.Configuration(config, sectionName: "AppLog")// (context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
// .UseSerilog(MondayConfiguration.Logger)
.ConfigureServices((hostContext, services) =>
{
services
.Configure<ServiceLocationOptions>(hostContext.Configuration.GetSection(key: nameof(ServiceLocationOptions)))
.Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(30));
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
ServiceLocationOptions? locationOptions = config.GetSection(nameof(ServiceLocationOptions)).Get<ServiceLocationOptions>();
string url = locationOptions?.HttpBase + "*:" + locationOptions?.Port;
webBuilder.UseUrls(url);
})
.UseSystemd();
}
Here below you find the main implementation of the service, I added a lot of XML to show you all the exception you may get when moving things around in the service and which methods should be implmented as a minimum to have the service to work.
If you remove all the variables that you don't understand, you will remain with a working skeleton of a windows service running simply a ping service. Try it.
/// <summary>
/// Main Worker class for managing the service inside SystemD.
/// </summary>
public partial class MondayService : BackgroundService
{
/// <summary>
/// Initializes a new instance of the <see cref="MondayService"/> class. Std ctr.
/// </summary>
/// <param name="mondayhub">
/// </param>
/// <param name="containerServer">
/// </param>
public MondayService(IHubContext<MondayHub, IMondayServiceHub> mondayhub,
IFeatureContainer containerServer)
{
_containerServer = containerServer;
_dbManager = _containerServer.DbManager;
_parser = _containerServer.Parser;
_syslogQueue = _containerServer.SyslogQueue;
_segmentManager = _containerServer.SegmentManager;
_orderManager = _containerServer.OrderManager;
while (!MondayConfiguration.SerilogFactoryReady)
{
Thread.Sleep(20);
}
// _logger = MondayConfiguration.LoggerFactory.CreateLogger("");
_logger = new LoggerConfiguration().ReadFrom.Configuration(MondayConfiguration.Configuration, sectionName: "AppLog").CreateLogger().ForContext("Origin", "MondayService");
_mondayHub = mondayhub;
}
/// <summary>
/// Setup activities for the Monday service.
/// </summary>
/// <param name="cancellationToken">
/// </param>
/// <returns>
/// </returns>
/// <exception cref="OverflowException">
/// <paramref><name>value</name></paramref> is less than <see cref="TimeSpan.MinValue"/>
/// or greater than <see cref="TimeSpan.MaxValue"/>.
/// -or- value is <see><cref>System.Double.PositiveInfinity</cref></see> .
/// -or- value is <see cref="double.NegativeInfinity"/>.
/// </exception>
/// <exception cref="AggregateException">
/// The task was canceled. The <see><cref>InnerExceptions</cref></see> collection
/// contains a <see cref="TaskCanceledException"/> object.
/// -or- An exception was thrown during the execution of the task. The
/// <see><cref>InnerExceptions</cref></see> collection contains information about the
/// exception or exceptions.
/// </exception>
/// <exception cref="TaskCanceledException">
/// The task has been canceled.
/// </exception>
/// <exception cref="ArgumentNullException">
/// The <paramref><name>function</name></paramref> parameter was <see langword="null"/>.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The <see cref="CancellationTokenSource"/> associated with <paramref
/// name="cancellationToken"/> was disposed.
/// </exception>
/// <exception cref="IOException">
/// destFileName already exists and overwrite is <see langword="false"/>.
/// -or- An I/O error has occurred, e.g. while copying the file across disk volumes.
/// </exception>
/// <exception cref="UnauthorizedAccessException">
/// The caller does not have the required permission.
/// </exception>
/// <exception cref="SecurityException">
/// The caller does not have the required permission.
/// </exception>
/// <exception cref="FileNotFoundException">
/// <paramref><name>sourceFileName</name></paramref> was not found.
/// </exception>
public override Task StartAsync(CancellationToken cancellationToken)
{
_logger.DebugInfo().Information("MondayService: Starting StartAsync");
var parserRenewed = false;
if (_parser is null)
{
_parser = new();
_parser.OnParserSocketDataArrived +=
(sender, e) => _containerServer.BroadcastParserSocketDataViaSignalR(sender, e);
parserRenewed = true;
Task.Run(ParserSubscriptions, cancellationToken);
_logger.DebugInfo().Information("MondayService: Instantiating again the parser inside StartAsync");
}
if (_dbManager is null || parserRenewed)
{
_dbManager = new(_parser);
_logger.DebugInfo().Information("MondayService: Instantiating again the db manager inside StartAsync");
_dbManager.ConnectToParserSocket();
_dbManager.OnFilteredSocketDataArrived +=
(sender, e) => _containerServer.BroadcastFilteredSocketDataViaSignalR(sender, e);
if (!_tagsDataSavedOnce)
{
// ReSharper disable once ExceptionNotDocumented
Tags.SaveAll();
_tagsDataSavedOnce = true;
}
}
if (_segmentManager is null)
{
_segmentManager = new(_parser, _dbManager);
_segmentManager.ConnectToParserSocket(_parser);
_segmentManager.OnSegmentClosure += (sender, e) => _containerServer.BroadcastSegmentDataViaSignalR(sender, e);
_logger.DebugInfo().Information("MondayService: Instantiating again the segment manager inside StartAsync");
}
if (_orderManager is null)
{
_orderManager = new(_parser);
_orderManager.OnOrderManagerEvent +=
(sender, e) => _containerServer.BroadcastOrderManagerEventsViaSignalR(sender, e);
_logger.DebugInfo().Information("MondayService: Instantiating again the order manager inside StartAsync");
}
_logger.DebugInfo().Information("MondayService: Completing StartAsync");
return base.StartAsync(cancellationToken);
}
/// <summary>
/// Graceful shutdown and disposal of Monday service (parser and database manager comprised).
/// </summary>
/// <param name="cancellationToken">
/// </param>
/// <returns>
/// </returns>
/// <exception cref="IOException">
/// destFileName already exists and overwrite is <see langword="false"/>.
/// -or- An I/O error has occurred, e.g. while copying the file across disk volumes.
/// </exception>
/// <exception cref="UnauthorizedAccessException">
/// The caller does not have the required permission.
/// </exception>
/// <exception cref="AggregateException">
/// The task was canceled. The <see><cref>InnerExceptions</cref></see> collection
/// contains a <see cref="TaskCanceledException"/> object.
/// -or- An exception was thrown during the execution of the task. The
/// <see><cref>InnerExceptions</cref></see> collection contains information about the
/// exception or exceptions.
/// </exception>
public override async Task StopAsync(CancellationToken cancellationToken)
{
_logger.DebugInfo().Information("MondayService: Starting StopAsync");
if (!_tagsDataSavedOnce)
{
Tags.SaveAll();
_tagsDataSavedOnce = true;
}
_logger.DebugInfo().Information("Stopping Monday Service hosted on Linux.");
if (_parser is not null) await _parser.UnsubscribeAllAsync();
foreach (string ex in Tags.Exchanges.ExchangeNames.ToList())
{
_parser?.DeactivateRest(ex);
}
_parser?.Dispose();
_dbManager?.Dispose();
_orderManager?.Dispose();
_segmentManager?.Dispose();
_logger.DebugInfo().Information("MondayService: Completing StopAsync");
await base.StopAsync(cancellationToken);
}
/// <summary>
/// Core loop of the service. Here all the assets are instantiated and managed up to
/// final disposal. This instantiates the SignalR service and manages it.
/// </summary>
/// <param name="stoppingToken">
/// </param>
/// <returns>
/// </returns>
/// <exception cref="TaskCanceledException">
/// The task has been canceled.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref><name>delay</name></paramref> represents a negative time interval other
/// than <see langword="TimeSpan.FromMilliseconds(-1)"/>.
/// -or- The <paramref><name>delay</name></paramref> argument's <see
/// cref="P:System.TimeSpan.TotalMilliseconds"/> property is greater than <see cref="int.MaxValue"/>.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// The provided <paramref><name>cancellationToken</name></paramref> has already been disposed.
/// </exception>
/// <exception cref="OverflowException">
/// <paramref><name>value</name></paramref> is less than <see cref="TimeSpan.MinValue"/>
/// or greater than <see cref="TimeSpan.MaxValue"/>.
/// -or- <paramref><name>value</name></paramref> is <see cref="double.PositiveInfinity"/>.
/// -or- <paramref><name>value</name></paramref> is <see cref="double.NegativeInfinity"/>.
/// </exception>
/// <exception cref="IOException">
/// destFileName already exists and overwrite is <see langword="false"/>.
/// -or- An I/O error has occurred, e.g. while copying the file across disk volumes.
/// </exception>
/// <exception cref="UnauthorizedAccessException">
/// The caller does not have the required permission.
/// </exception>
/// <exception cref="SecurityException">
/// The caller does not have the required permission.
/// </exception>
/// <exception cref="FileNotFoundException">
/// <paramref><name>sourceFileName</name></paramref> was not found.
/// </exception>
/// <exception cref="DirectoryNotFoundException">
/// The specified path is invalid (for example, it is on an unmapped drive).
/// </exception>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.DebugInfo().Information("Monday Service starting at {Now}", DateTimeOffset.Now);
while (!stoppingToken.IsCancellationRequested)
{
Task tlog = FetchLogAsync(stoppingToken);
Task? tping = null;
int seconds = DateTimeOffset.Now.Second;
int minutes = DateTimeOffset.Now.Minute;
// logging a ping every 5 minutes
if (seconds < 5 && minutes % 1 == 0)
{
tping = Ping();
if (!_tagsDataSavedOnce)
{
Tags.SaveAll();
_tagsDataSavedOnce = true;
}
}
// looping every 5 seconds
var tLoop = Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
if (tping is null)
{
await Task.WhenAll(tlog, tLoop);
}
else
{
await Task.WhenAll(tlog, tLoop, tping);
}
}
_logger.DebugInfo().Information("Monday Service stopping at {Now}", DateTimeOffset.Now);
}
[MustUseReturnValue]
private Task FetchLogAsync(CancellationToken stoppingToken)
{
var ok = true;
while (ok)
{
try
{
ok = _containerServer.SyslogQueue.Reader.TryRead(
out (LogLevel lvl, string line) item);
if (ok)
{
switch (item.lvl)
{
case LogLevel.Trace:
case LogLevel.Debug:
_logger.DebugInfo().Debug("{FetchedMessage}", item.line);
break;
case LogLevel.Information:
_logger.DebugInfo().Information("{FetchedMessage}", item.line);
break;
case LogLevel.Warning:
_logger.DebugInfo().Warning("{FetchedMessage}", item.line);
break;
case LogLevel.Error:
_logger.DebugInfo().Error("{FetchedMessage}", item.line);
break;
case LogLevel.Critical:
_logger.Fatal("{FetchedMessage}", item.line);
break;
case LogLevel.None:
break;
}
}
if (stoppingToken.IsCancellationRequested)
{
ok = false;
}
}
catch
{
ok = false;
}
}
return Task.CompletedTask;
}
private Task<CallResult<UpdateSubscription>> ParserSubscriptions()
{
Guard.Against.Null(nameof(_parser));
return _parser!.SubscribeFromSettingsAsync();
}
private async Task Ping()
{
await _containerServer.SyslogQueue.Writer.WriteAsync((
LogLevel.Information,
$"Monday Service active at: {DateTime.UtcNow.ToLocalTime()}"));
}
/// <summary>
/// This is a debug utility to check whether the service creates too many ThreaPpool threads.
/// </summary>
public static class ProcessTracker
{
static ProcessTracker()
{
}
/// <summary>
/// See https://stackoverflow.com/questions/31633541/clrmd-throws-exception-when-creating-runtime/31745689#31745689
/// </summary>
/// <returns>
/// </returns>
public static string Scan()
{
StringBuilder sb = new();
StringBuilder answer = new();
answer.Append("Active Threads").Append(Environment.NewLine);
// Create the data target. This tells us the versions of CLR loaded in the target process.
int countThread = 0;
var pid = Environment.ProcessId;
using (var dataTarget = DataTarget.AttachToProcess(pid, 5000, AttachFlag.Passive))
{
// Note I just take the first version of CLR in the process. You can loop over
// every loaded CLR to handle the SxS case where both desktop CLR and .Net Core
// are loaded in the process.
ClrInfo version = dataTarget.ClrVersions[0];
var runtime = version.CreateRuntime();
// Walk each thread in the process.
foreach (ClrThread thread in runtime.Threads)
{
try
{
sb = new();
// The ClrRuntime.Threads will also report threads which have recently
// died, but their underlying data structures have not yet been cleaned
// up. This can potentially be useful in debugging (!threads displays
// this information with XXX displayed for their OS thread id). You
// cannot walk the stack of these threads though, so we skip them here.
if (!thread.IsAlive)
continue;
sb.Append("Thread ").AppendFormat("{0:X}", thread.OSThreadId).Append(':');
countThread++;
// Each thread tracks a "last thrown exception". This is the exception
// object which !threads prints. If that exception object is present, we
// will display some basic exception data here. Note that you can get
// the stack trace of the exception with ClrHeapException.StackTrace (we
// don't do that here).
ClrException? currException = thread.CurrentException;
if (currException is ClrException ex)
{
sb.Append("Exception: ")
.AppendFormat("{0:X}", ex.Address)
.Append(" (").Append(ex.Type.Name)
.Append("), HRESULT=")
.AppendFormat("{0:X}", ex.HResult)
.AppendLine();
}
// Walk the stack of the thread and print output similar to !ClrStack.
sb.AppendLine(" ------> Managed Call stack:");
var collection = thread.EnumerateStackTrace().ToList();
foreach (ClrStackFrame frame in collection)
{
// Note that CLRStackFrame currently only has three pieces of data:
// stack pointer, instruction pointer, and frame name (which comes
// from ToString). Future versions of this API will allow you to get
// the type/function/module of the method (instead of just the
// name). This is not yet implemented.
sb.Append(" ").Append(frame).AppendLine();
}
}
catch
{
//skip to the next
}
finally
{
answer.Append(sb);
}
}
}
answer.Append(Environment.NewLine).Append(" Total thread listed: ").Append(countThread);
return answer.ToString();
}
}
}
The main core loop is pinging a queue for logging. It is just a way to let something run over the service permanently while other classes do their work (in this case they are almost all based on EAP).
Definitely, look at MSDN at first. It seems that you are missing the basics.
[1]: https://learn.microsoft.com/en-us/dotnet/core/extensions/workers
I ended up writing it as a Windows Service. I simply inherited ServiceBase and wrote Start and Stop methods. The start method starts a Timer which calls a Method with the infinite loop every minute.
See: Error 1053: Service did not respond in time
There is a similar question but my questions seems more general
I've went through a few git repos and noticed Request/Response wrappers over HttpRequestMessage like CryptoExchange.Net.Requests.Request
/// <summary>
/// Request object, wrapper for HttpRequestMessage
/// </summary>
public class Request : IRequest
{
private readonly HttpRequestMessage request;
private readonly HttpClient httpClient;
/// <summary>
/// Create request object for web request
/// </summary>
/// <param name="request"></param>
/// <param name="client"></param>
/// <param name="requestId"></param>
public Request(HttpRequestMessage request, HttpClient client, int requestId)
{
httpClient = client;
this.request = request;
RequestId = requestId;
}
/// <inheritdoc />
public string? Content { get; private set; }
/// <inheritdoc />
public string Accept
{
set => request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(value));
}
/// <inheritdoc />
public HttpMethod Method
{
get => request.Method;
set => request.Method = value;
}
/// <inheritdoc />
public Uri Uri => request.RequestUri;
/// <inheritdoc />
public int RequestId { get; }
/// <inheritdoc />
public void SetContent(string data, string contentType)
{
Content = data;
request.Content = new StringContent(data, Encoding.UTF8, contentType);
}
/// <inheritdoc />
public void AddHeader(string key, string value)
{
request.Headers.Add(key, value);
}
/// <inheritdoc />
public Dictionary<string, IEnumerable<string>> GetHeaders()
{
return request.Headers.ToDictionary(h => h.Key, h => h.Value);
}
/// <inheritdoc />
public void SetContent(byte[] data)
{
request.Content = new ByteArrayContent(data);
}
/// <inheritdoc />
public async Task<IResponse> GetResponseAsync(CancellationToken cancellationToken)
{
return new Response(await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false));
}
}
Previously to send request and get response WebRequest/WebResponse types were used, with their implementations for Http: HttpWebRequest/HttpWebResponse.
I assume HttpRequestMessage was introduced with .NET Core, so it's kind of better abstraction and platform agnostic implementation
Should we now use the newest approach with HttpRequestMessage and think HttpWebRequest is obsolete (but it is not marked as obsolete yet)
HttpWebRequest (or in general anything derived from WebRequest) has been widely considered obsolete ever since HttpClient was introduced (more than a decade now).
But it was only enforced in .NET Core via attributes like,
https://github.com/dotnet/runtime/blob/e18a77feff92fe6cdf8b891aa3b44fb1acb972d1/src/libraries/System.Net.Requests/ref/System.Net.Requests.cs#L180
Note that using HttpClient on .NET Framework was rather troublesome for years, and maybe that's why Microsoft didn't push everyone there.
Having the below class
/// <summary>
/// An unsuccessful HTTP result.
/// </summary>
public class UnsuccessfulActionResult : IActionResult, IHttpStatusCodeResult, IErrors
{
/// <inheritdoc />
/// <summary>
/// The HTTP status code.
/// </summary>
public HttpStatusCode StatusCode { get; }
/// <summary>
/// The corresponding error messages, if any.
/// </summary>
public IReadOnlyCollection<string> Errors { get; }
/// <summary>
/// The request Transaction ID.
/// </summary>
public string TransactionId { get; }
/// <summary>
/// Default ctor.
/// </summary>
/// <param name="transactionId">The request Transaction ID.</param>
/// <param name="errors">The corresponding error messages, if any.</param>
/// <param name="httpStatusCode"></param>
public UnsuccessfulActionResult(
string transactionId,
HttpStatusCode httpStatusCode,
IReadOnlyCollection<string> errors = null)
{
TransactionId = transactionId;
StatusCode = httpStatusCode;
Errors = errors ?? new List<string>();
}
/// <summary>
/// Convenient method to create an unsuccessful response model.
/// </summary>
/// <param name="statusCode"></param>
/// <returns></returns>
protected UnsuccessfulResponseModel CreateResponseModel(int statusCode)
{
return new UnsuccessfulResponseModel(TransactionId, statusCode, Errors);
}
/// <inheritdoc />
public virtual Task ExecuteResultAsync(ActionContext context)
{
var objectResult = new ObjectResult(CreateResponseModel((int)StatusCode))
{
StatusCode = (int)StatusCode
};
return objectResult.ExecuteResultAsync(context);
}
}
I need to test this class to make sure that ActionContext has the response provided in overridden ExecuteResultAsync.
Just started going deep into ASP.NET Core 2.0 and missing some parts, in Web API 2 I would just check the HttpResponseMessage returned in the equivalent method of IHttpActionResult.
Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken
)
I have Google authentication working in my app through OAuthWebSecurity. Every time I login, Google re-prompts for permissions. How can I prevent Google from re-prompting every time?
I am currently using a custom OpenIdClient to get extra data from Google (source), but this also happens with the default client.
internal static class AuthConfig
{
public static void RegisterOpenAuth(ConfigurationWrapper configuration)
{
//OAuthWebSecurity.RegisterGoogleClient(); // using this has the exact same problem as my custom code
OAuthWebSecurity.RegisterClient(new GoogleCustomClient(), "Google", null);
}
}
/// <summary>
/// Represents Google OpenID client.
/// </summary>
public class GoogleCustomClient : OpenIdClient
{
#region Constructors and Destructors
public GoogleCustomClient()
: base("google", WellKnownProviders.Google)
{
}
#endregion
#region Methods
/// <summary>
/// Gets the extra data obtained from the response message when authentication is successful.
/// </summary>
/// <param name="response">
/// The response message.
/// </param>
/// <returns>A dictionary of profile data; or null if no data is available.</returns>
protected override Dictionary<string, string> GetExtraData(IAuthenticationResponse response)
{
var fetchResponse = response.GetExtension<FetchResponse>();
if (fetchResponse == null) return null;
var extraData = new Dictionary<string, string>
{
{"email", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.Email)},
{"country", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.HomeAddress.Country)},
{"firstName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.First)},
{"lastName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.Last)}
};
return extraData;
}
/// <summary>
/// Called just before the authentication request is sent to service provider.
/// </summary>
/// <param name="request">
/// The request.
/// </param>
protected override void OnBeforeSendingAuthenticationRequest(IAuthenticationRequest request)
{
// Attribute Exchange extensions
var fetchRequest = new FetchRequest();
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Contact.HomeAddress.Country);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Name.First);
fetchRequest.Attributes.AddRequired(WellKnownAttributes.Name.Last);
request.AddExtension(fetchRequest);
}
#endregion
}
I'm new to this forum and to have a question about await/async use in Xamarin (Also the first time I work with).
I am working for my internship on a project using Xamarin, PCL, MvvmCross.
In my PCL im do a postrequest to a WCF service to login in my application. In WP8 everything just works fine, but when I am running my application on Android the response is always null.
Below you can find my httpclient class. The method with the post is InternalPostAsync
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using Anton.Mobile.Shared.Infrastructure;
using System.Net;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
namespace Anton.Mobile.Shared.Data
{
public class AntonClient
{
#region static
/// <summary>
/// Base Uri of the Ria service (e.g. http://example.com/)
/// </summary>
private static readonly Uri _baseUri = new Uri(Config.BaseUri);
/// <summary>
/// Last cookie response header (Session, authentication, ...)
/// </summary>
private static string _cookieHeader = null;
/// <summary>
/// Lock object for read/write <para>_cookieHeader</para>
/// </summary>
private static object _lockObj = new object();
#endregion
#region protected
/// <summary>
/// Creates a client
/// </summary>
/// <param name="container">Cookie container to use</param>
/// <returns>HttpClient</returns>
protected virtual HttpClient CreateClient(CookieContainer container)
{
//set container on handler for tracking cookies between request-response
var handler = new HttpClientHandler()
{
CookieContainer = container,
UseCookies = true,
UseDefaultCredentials = false,
};
//Create client and set the base address
var cl = new HttpClient(handler)
{
BaseAddress = _baseUri
};
if (!string.IsNullOrEmpty(_cookieHeader))
{
cl.DefaultRequestHeaders.Add("Cookies", _cookieHeader);
}
return cl;
}
/// <summary>
/// Creates a JSON content request
/// </summary>
/// <param name="jsonContent">JSON value</param>
/// <returns>JSON content</returns>
protected virtual HttpContent CreateRequestContent(string jsonContent)
{
var content = new StringContent(jsonContent,Encoding.UTF8,"application/json");
//content.Headers.Add("ContentType", "application/json");
return content;
}
/// <summary>
/// Save cookies <para>_cookieHeader</para>
/// </summary>
/// <param name="container">cookie container</param>
protected void ParseCookies(HttpResponseMessage msg)
{
IEnumerable<string> values;
if (!msg.Headers.TryGetValues("Set-Cookie", out values) || !values.Any())
return;
//var cookies = container.GetCookieHeader(_baseUri);
var cs = new List<string>();
foreach (var v in values)
{
string[] vs = v.Split(new char[] { ';' });
string[] value = vs[0].Split(new char[] { '=' });
container.Add(new Uri("Http://initesting"), new Cookie(value[0], value[1]));
cs.Add(string.Format("{0}={1}", value[0], value[1]));
}
lock (_lockObj)
{
_cookieHeader = string.Join(";", cs.ToArray());
}
}
private static CookieContainer container = new CookieContainer();
/// <summary>
/// Create a new cookie container from <para>_cookieHeaders</para>
/// </summary>
/// <returns>Cookie container</returns>
protected CookieContainer CreateCookieContainer()
{
//lock (_lockObj)
//{
// if (!string.IsNullOrEmpty(_cookieHeader))
// {
// foreach (var header in _cookieHeader.Split(new char[] { ';' }))
// {
// container.SetCookies(_baseUri, header);
// }
// }
//}
return container;
}
/// <summary>
/// Executes a POST HTTP Request
/// </summary>
/// <param name="jsonContent">POST JSON content</param>
/// <param name="uri">Service uri</param>
/// <returns>Response content as string (JSON)</returns>
protected virtual async Task<string> InternalPostAsync(string jsonContent, Uri uri)
{
var container = CreateCookieContainer();
using (var client = CreateClient(container))
{
var content = CreateRequestContent(jsonContent);
var response = await client.PostAsync(uri, content);
if (response.StatusCode != HttpStatusCode.OK)
{
return null; //todo
}
ParseCookies(response);
return await response.Content.ReadAsStringAsync();
}
}
/// <summary>
/// Executes a GET HTTP Request
/// </summary>
/// <param name="uri">Service uri</param>
/// <returns>Response content as string (JSON)</returns>
protected virtual async Task<string> InternalRequestAsync(Uri uri)
{
var container = CreateCookieContainer();
using (var client = CreateClient(container))
{
HttpResponseMessage response = await client.GetAsync(uri);
if (response.StatusCode != HttpStatusCode.OK)
{
return null;
}
ParseCookies(response);
return await response.Content.ReadAsStringAsync();
}
}
#endregion protected
#region public
/// <summary>
/// Executes a POST HTTP Request for a given Request key
/// </summary>
/// <typeparam name="TRequest">Request Type</typeparam>
/// <typeparam name="TResult">Result Type</typeparam>
/// <param name="request">Request POST value to JSON serializing</param>
/// <param name="key">Unique Request Key</param>
/// <returns>Deserialized POST response content of type TResult</returns>
public async Task<TResult> PostAsync<TRequest, TResult>(TRequest request, RequestKey key)
where TRequest : class
where TResult : class
{
try
{
var uri = RequestMap.GetUri(key);
string jsonResult = await InternalPostAsync(request.SerializeJson(), uri);
return jsonResult.DeserializeJson<TResult>();
}
catch (Exception)
{
//todo
}
return default(TResult);
}
/// <summary>
/// Executes a POST HTTP Request for a given service uri
/// </summary>
/// <typeparam name="TRequest">Request Type</typeparam>
/// <param name="request">Request POST value to JSON serializing</param>
/// <param name="uri">Service URI</param>
/// <returns>Deserialized POST response content of type dynamic</returns>
public async Task<dynamic> PostAsync<TRequest>(TRequest request, string uri)
{
try
{
string jsonResult = await InternalPostAsync(request.SerializeJson(), new Uri(uri, UriKind.Absolute));
return jsonResult.DynamicJson();
}
catch (Exception)
{
//todo
}
return null;
}
/// <summary>
/// Executes a GET HTTP Request for a givin key and query string parameter info
/// </summary>
/// <typeparam name="TResponse">Response Type</typeparam>
/// <param name="key">Unique request key</param>
/// <param name="queryString">Querystring info</param>
/// <returns>Deserialized POST response content of type TResult</returns>
public async Task<TResponse> RequestAsync<TResponse>(RequestKey key, IDictionary<string, string> queryString = null)
{
try
{
string jsonResult = await InternalRequestAsync(RequestMap.GetUri(key, queryString));
var dynamicResult = jsonResult.DynamicJson();
var item = (dynamicResult as JObject)[RequestMap.GetValue(key) + "Result"]["RootResults"].First; //todo: better solution for this
return item.ToObject<TResponse>();
}
catch (Exception)
{
//todo
}
return default(TResponse);
}
#endregion public
}
}
Regarding the website of Xamarin async/await is supported, but I did not find any similar problems. I hope you can help me.
I have solved my problem.
In Android, when you add the NuGet package with the HTTP Libraries, something went wrong when adding the references. You have to add the references mannualy to solve it. I also editted my Uri, now it works with the IP-address in the Uri.