Home > Net >  C # HttpWebRequest , Fire and forget an API call
C # HttpWebRequest , Fire and forget an API call

Time:10-28

In my WCF service, I have to make a call to an API, where I wanted to do a Fire and Forget implementation. And If possible just capture the errors if any.(That's fine too , if not an option)

I am planning to do the following implementation, what are the issues it could lead to? By doing the following implementation is going to leave a huge number of open connections. Or what could be the issue? Please help in understanding how in a better way this can be implemented.

void SendRequest(inputs)
 {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
        request.Method = "POST";
        request.ContentType = "application/xml";

        byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);
        using (Stream requestStream = request.GetRequestStream())
        {
            requestStream.Write(requestBytes, 0, requestBytes.Length);
        }

        request.GetResponseAsync();
 } 
 Main()
   { 
       try
          SendRequest(inputs);
       catch ex
          log ex;
   }

CodePudding user response:

Please note that it's not best practice not to use fire and forget, especially if this a core layer of the application and you might miss important exceptions. When you use this technique you have to remember that the following happens:

  1. Exception will be fail silently without any chance of catching them. normally you will want to log them or get a notification.
  2. You have no idea when the code completes,
  3. Since You don't need the code to complete and it might may not run to you would have no notification that it failed to complete.

A good case scenario for using this technique could be for updating a cache for an example.

Having said that, you could use the following techniques:

NET 4.5 allows us to use it via Task.Run

Task.Run(() => FireAndForget());

You could also start a thread with parameterless lambda:

(new Thread(() => { 
 FireAndForget(); 
 }) { 
   Name = "Running Work Thread (FireAndForget)",
   Priority = ThreadPriority.BelowNormal 
}).Start();

CodePudding user response:

First, make fully async version of your code

using System.Threading;

public async Task<System.Net.WebResponse> SendRequestAsync(
    string inputXML, string url, CancellationToken cancellationToken)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = "POST";
    request.ContentType = "application/xml";

    byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);

    // GetRequestStreamAsync is a lightweight operation I assume
    // so we don't really need any cancellation
    using (Stream requestStream = await request.GetRequestStreamAsync())
    {
        // Idk if we really need cancellation here, but just in case of big request we might need to
        await requestStream.WriteAsync(
            requestBytes, 0, requestBytes.Length, cancellationToken);
    }
    
    // Close any long-running request
    using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
    {
        var response = await request.GetResponseAsync();
        cancellationToken.ThrowIfCancellationRequested();
        return response;
    }
} 

Let's create an async void method, but make it safe. It will basically execute in "fire-and-forget" manner.

public async void DoWork(string inputXML, string url, CancellationToken ct)
{
    try
    {
        using(var response = await SendRequestAsync(inputXML, url, ct))
        {
            var httpResponse = (HttpWebResponse) response;
            // Use 201 Created or whatever you need
            if (httpResponse.StatusCode != HttpStatusCode.Created)
            { 
                // TODO: handle wrong status code
            }
        }
    }
    catch (Exception e)
    {
        if (ct.IsCancellationRequested)
        {
            Console.WriteLine("Cancelled");
        }
        else
        {
            // TODO: handle exception
        }
    }
}

private static CancellationTokenSource _cts = new CancellationTokenSource();

public static void Main (string[] args)
{
    DoWork("xml string", "example.com", cts.Token);
    Console.WriteLine("Boom!");
    if (Console.ReadLine() == "exit")
    {
        // Cancel the damn job
        cts.Cancel();
    }
}

It's important to handle all errors from inside a DoWork, because following will not work

// Warning, will NOT catch an exception
public static void Main (string[] args)
{
    try 
    {
        DoWork("xml string", "example.com"); 
    }
    catch (Exception e)
    {
    }
}

EDIT: OP requested cancellation so I added cancellation

  • Related