diff --git a/AsyncResponse/Controllers/AsyncController.cs b/AsyncResponse/Controllers/AsyncController.cs index 9df07c4..0cea1b7 100644 --- a/AsyncResponse/Controllers/AsyncController.cs +++ b/AsyncResponse/Controllers/AsyncController.cs @@ -13,67 +13,67 @@ namespace AsyncResponse.Controllers { public class AsyncController : ApiController { - //State dictionary for sample - stores the state of the working thread + // Create a state dictionary that stores the state for the working thread in this sample private static Dictionary runningTasks = new Dictionary(); - /// - /// This is the method that starts the task running. It creates a new thread to complete the work on, and returns an ID which can be passed in to check the status of the job. - /// In a real world scenario your dictionary may contain the object you want to return when the work is done. + /// This method starts the task, creates a new thread to do work, + /// and returns an ID that you can pass to the Logic Apps engine for checking job status. + /// In a real world scenario, your dictionary can contain the object that you want to return after work has finished. /// /// HTTP Response with needed headers [HttpPost] [Route("api/startwork")] public async Task longrunningtask() { - Guid id = Guid.NewGuid(); //Generate tracking Id - runningTasks[id] = false; //Job isn't done yet - new Thread(() => doWork(id)).Start(); //Start the thread of work, but continue on before it completes + Guid id = Guid.NewGuid(); // Generate tracking ID for checking job status + runningTasks[id] = false; // Job not done yet + new Thread(() => doWork(id)).Start(); // Start the thread to do work, but continue on before the job completes HttpResponseMessage responseMessage = Request.CreateResponse(HttpStatusCode.Accepted); - responseMessage.Headers.Add("location", String.Format("{0}://{1}/api/status/{2}", Request.RequestUri.Scheme, Request.RequestUri.Host, id)); //Where the engine will poll to check status - responseMessage.Headers.Add("retry-after", "20"); //How many seconds it should wait (20 is default if not included) + responseMessage.Headers.Add("location", String.Format("{0}://{1}/api/status/{2}", Request.RequestUri.Scheme, Request.RequestUri.Host, id)); // The URL to poll for job status + responseMessage.Headers.Add("retry-after", "20"); // The number of seconds to wait before polling for job status again. The default is 20 seconds when not included. return responseMessage; } - /// - /// This is where the actual long running work would occur. + /// This method performs the actual long-running work. /// /// private void doWork(Guid id) { Debug.WriteLine("Starting work"); - Task.Delay(120000).Wait(); //Do work will work for 120 seconds) + Task.Delay(120000).Wait(); // Do work for 120 seconds. Debug.WriteLine("Work completed"); - runningTasks[id] = true; //Set the flag to true - work done + runningTasks[id] = true; // Set flag to "true" when work is done. } /// - /// Method to check the status of the job. This is where the location header redirects to. + /// This method checks the job's status and is also the place to where the "location" header redirects. /// /// /// [HttpGet] [Route("api/status/{id}")] - [Swashbuckle.Swagger.Annotations.SwaggerResponse(HttpStatusCode.BadRequest, "No job exists with the specified id")] + [Swashbuckle.Swagger.Annotations.SwaggerResponse(HttpStatusCode.BadRequest, "No job exists with the specified ID")] [Swashbuckle.Swagger.Annotations.SwaggerResponse(HttpStatusCode.Accepted, "The job is still running")] [Swashbuckle.Swagger.Annotations.SwaggerResponse(HttpStatusCode.OK, "The job has completed")] public HttpResponseMessage checkStatus([FromUri] Guid id) { - //If the job is complete + // If the job is done, return "200 OK" status with response payload (data output). if(runningTasks.ContainsKey(id) && runningTasks[id]) { runningTasks.Remove(id); - return Request.CreateResponse(HttpStatusCode.OK, "Some data could be returned here"); + return Request.CreateResponse(HttpStatusCode.OK, "Can return some data here"); } - //If the job is still running + // If the job is still running, return "202 ACCEPTED" status, the URL to poll for job status, and the number of seconds to wait before checking status again. else if(runningTasks.ContainsKey(id)) { HttpResponseMessage responseMessage = Request.CreateResponse(HttpStatusCode.Accepted); - responseMessage.Headers.Add("location", String.Format("{0}://{1}/api/status/{2}", Request.RequestUri.Scheme, Request.RequestUri.Host, id)); //Where the engine will poll to check status + responseMessage.Headers.Add("location", String.Format("{0}://{1}/api/status/{2}", Request.RequestUri.Scheme, Request.RequestUri.Host, id)); // The URL where the engine can poll for job status responseMessage.Headers.Add("retry-after", "20"); return responseMessage; } + // No job with the specified ID was found. else { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "No job exists with the specified ID"); diff --git a/README.md b/README.md index 12866f5..e5bd337 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,22 @@ [![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://azuredeploy.net/) # Logic Apps Async Response Sample -Sample of how to do an HTTP Async Response pattern to work with Azure Logic Apps. See the AsyncController for the logic. The Logic Apps engine will timeout by default after 1-2 minutes of an open HTTP Request. By setting up this async pattern you can keep the Logic Apps engine waiting for a task for much longer (as long as you have data stored for your flow - which is up to 1 year for Premium plans). -## How it Works ## +This sample shows how to create an HTTP Async Response pattern that works for Azure Logic Apps. +For logic details, see the AsyncController.cs file. By default, the Logic Apps engine times out +after 1-2 minutes for an open HTTP Request. When you set up this async pattern, +you can make the Logic Apps engine wait for a task that takes longer to finish +(as long as you have data stored for your flow, which is up to 1 year for Premium plans). -When you get the initial request to start work, you start a thread with the long-running task, and immediatly return an HTTP Response "202 Accepted" with a location header. The location header points to the URL for the Logic Apps to check the status of the long-running job. By default the engine will check every 20 seconds, but you can also add a "Retry-after" header to specify the amount of seconds until the next poll. +## How the sample works -After the alloted time (20 seconds), the engine will poll the URL on the location header. If the long-running job is still going, you should return another "202 Accepted" with a location header. If the job has completed, you should return a "200 OK", along with any relevant data. This is what the Logic Apps engine will continue the workflow with. +When you get the initial request to start work, start a thread with the long-running task, +and immediatly return an HTTP Response "202 Accepted" status with a location header. +The location header points to the URL where the Logic Apps engine can check status for the long-running job. + +By default, the engine checks every 20 seconds, but you can also add a "Retry-after" header +that specifies the number of seconds until the next poll. After the given time (20 seconds), +the engine polls the URL on the location header. If the long-running job is still working, +you should return another "202 Accepted" status with a location header. +If the job has finished, you should return a "200 OK" status, along with any relevant data. +The Logic Apps engine uses this data to continue the workflow.