Search code examples
c#wcfhttpwebrequestfire-and-forget

C # HttpWebRequest , Fire and forget an API call


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;
}

Solution

  • 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