logic apps web hook to chalkboard API timeout error - timeout

How do I change the timeout duration in logic apps web hook and also in chalkboard API.
The error message I get is.
"message": "Http request failed: the server did not respond within the timeout limit. Please see logic app limits at https://aka.ms/logic-apps-limits-and-config#http-limits"

You can refer to Perform long-running tasks with the webhook action pattern.
After understanding the webhook pattern, you need to design some code, you can refer to the following sample:
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;
using System.Threading;
using System.Net.Http;
using System;
namespace HttpToQueueWebhook
{
public static class HttpTrigger
{
[FunctionName("HttpTrigger")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "post")]HttpRequest req,
TraceWriter log,
[Queue("process")]out ProcessRequest process)
{
log.Info("Webhook request from Logic Apps received.");
string requestBody = new StreamReader(req.Body).ReadToEnd();
dynamic data = JsonConvert.DeserializeObject(requestBody);
string callbackUrl = data?.callbackUrl;
//This will drop a message in a queue that QueueTrigger will pick up
process = new ProcessRequest { callbackUrl = callbackUrl, data = "some data" };
return new AcceptedResult();
}
public static HttpClient client = new HttpClient();
/// <summary>
/// Queue trigger function to pick up item and do long work. Will then invoke
/// the callback URL to have logic app continue
/// </summary>
[FunctionName("QueueTrigger")]
public static void Run([QueueTrigger("process")]ProcessRequest item, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {item.data}");
//Thread.Sleep(TimeSpan.FromMinutes(3));
//ProcessResponse result = new ProcessResponse { data = "some result data" };
//handle your business here.
client.PostAsJsonAsync<ProcessResponse>(item.callbackUrl, result);
}
}
public class ProcessRequest
{
public string callbackUrl { get; set; }
public string data { get; set; }
}
public class ProcessResponse
{
public string data { get; set; }
}
}
The above code will first save your callbackUrl and the passed data to the queue, and then return the result of 202 to the logic app.
The QueueTrigger function will be triggered, and you can handle your business here.
You can call your http function like this in Azure logic app:
This solution can help you solve the http timeout problem. For more details, you can refer to this article.

Related

Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.HubConnectionContext' while attempting to activate an api controller

I'm developing a web application with ASP.NET Core (MVC) that will display some information to a group of users. They will be authenticated using Windows Athentication. I recently started using SignalR because I want them to receive push notifications based on certain actions that are triggered from another program that is written in Python. That Python program send HTTP post requests to an API controller within the web app. However I want those notifications to be sent to specific users.
Here's my post method in the api controller:
[HttpPost]
public void Post([FromBody] Notice notice)
{
//_pushHub.Clients.All.SendAsync("ReceiveMessage", notice.Title, notice.Body);
_pushHub.Clients.User(_nuidProvider.GetUserId(_connection)).SendAsync("ReceiveMessage", notice.Title, notice.Body);
}
It receives an object of class Notice which has two fields: Title and Body.
Then I send those values by using the method SendAsync(). When I execute it for all clients, it gets sent successfully. However, since I need it to be sent to specific users, I tried following these instructions: https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0
I created this class as suggested:
namespace MyApp.Services
{
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
}
Then I added this to the StartUp at ConfigureServices:
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddSignalR();
And lastly, I modified the JS file to include the sentence: options.UseDefaultCredentials = true; as suggested in the instructions:
const conn = new signalR.HubConnectionBuilder().withUrl('/pushHub', options => {
options.UseDefaultCredentials = true;
}).build();
conn.on('ReceiveMessage', (title, body) => {
const t = title.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
const b = body.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
const date = new Date().toLocaleTimeString();
const msg = date + ' ' + t + ' ' + b;
const li = document.createElement('li');
li.innerHTML = msg;
document.getElementById('msgsList').appendChild(li);
Push.create(title, {
body: body,
timeout: 9000
});
});
conn.start().catch(err => console.error(err.toString()));
The complete api controller looks like this, but I'm not sure if I'm doing it right:
namespace MyApp.Controllers.api
{
[Route("api/[controller]")]
[ApiController]
public class NoticesController : ControllerBase
{
private readonly IHubContext<PushHub> _pushHub;
private readonly IUserIdProvider _nuidProvider;
private readonly HubConnectionContext _connection;
public NoticesController(IHubContext<PushHub> pushHub, IUserIdProvider nuidProvider, HubConnectionContext connection)
{
_pushHub = pushHub;
_nuidProvider = nuidProvider;
_connection = connection;
}
// POST api/<NoticesController>
[HttpPost]
public void Post([FromBody] Notice notice)
{
//_pushHub.Clients.All.SendAsync("ReceiveMessage", notice.Title, notice.Body);
_pushHub.Clients.User(_nuidProvider.GetUserId(_connection)).SendAsync("ReceiveMessage", notice.Title, notice.Body);
}
}
}
I think I'm not passing a proper connection object to the GetUserId method.
Any advice will be completely appreciated. Thanks!

Consuming ASP.NET Web API REST Service in ASP.NET MVC using HttpClient

I have an ASP.NET MVC application from which I would like to consume an ASP.NET Web API REST service.
So I have found a piece of code here.
In my case I would like to call ASP.NET Web API method (DumpIntoFile) from a method in a class in my ASP.NET MVC app (this class is not a controller). I need the execution not to continue until call to DumpIntoFile is completed and finished. How can I do it? I have never used async and await so I do not understand at all how they work.
public void GetData()
{
Warehouse myData = new Warehouse();
myData.id = 1111;
myData.name = blabla;
string path = "c:\temp";
string filename = "myData.dat";
// Stuff here
this.DumpWarehouseDataIntoFile(myData, path, filename);
// Some other stuff here
}
public async void DumpWarehouseDataIntoFile(Warehouse myData, string path, string filename) // See Warehouse class later in this post
{
//Hosted web API REST Service base url
string Baseurl = "http://XXX.XXX.XX.X:YYYY/";
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Serialize parameter to pass to the asp web api rest service
string jsonParam = Newtonsoft.JsonConvert.SerializeObject(myData);
//Sending request to find web api REST service resource using HttpClient
// How can I pass parameters jsonParam, path and filename to below call??????? Concatenating it?
HttpResponseMessage Res = await client.GetAsync("api/Warehouse/DumpIntoFile");
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
// Some other sftuff here
}
}
}
The Warehouse object is as below:
public class Warehouse {
public Warehouse();
public int id { get; set; }
public string name { get; set; }
// some other stuff here
}
And web api rest controller with method:
public class MyWebApiController : ApiController
{
public bool DumpIntoFile(string data, string path, string filename)
{
bool result;
Warehouse myData = JsonConvert.DeserializeObject<Warehouse>(data);
string myPath = path;
string myFilename = filename;
// Here rest of code
return result;
}
}
Also I am trying to do below:
Avoid to hard-code the URL because if the ASP.NET Web API REST service is published in another server in a future, then I need to touch code, change it and publish again my ASP.NET MVC application. Maybe putting a the url in the web.config file and read from there?
How can I pass the parameters jsonParam, path and filename to the web api rest service? concatenating it?
NOTES:
I am using NET 4.5 and Visual Studio 2013.
How can I do it? I have never used async and await so I do not understand at all how they work.
I recommend you read my async intro and follow up with async best practices. You'll learn two key things:
Avoid async void.
It's normal for async/await to "grow" through your codebase.
In your case, DumpWarehouseDataIntoFile should be async Task instead of async void. And that means GetData needs to await the task returned from DumpWarehouseDataIntoFile. Which means GetData should also be async Task. Which means things calling GetData should use await, etc.
Side note: By convention, asynchronous methods should end in Async.
public async Task GetDataAsync()
{
Warehouse myData = new Warehouse();
myData.id = 1111;
myData.name = blabla;
string path = "c:\temp";
string filename = "myData.dat";
// Stuff here
await this.DumpWarehouseDataIntoFileAsync(myData, path, filename);
// Some other stuff here
}
public async Task DumpWarehouseDataIntoFileAsync(Warehouse myData, string path, string filename)

MVC optional body parameter

I am trying to wire up a webhook from a 3rd party system.
When creating the subscription it hits the URL i provide and requires a validated token returned to create the hook.
When the event is triggered the hook posts to the same URL i provided with data in the body.
How can I get a Core 2.1 MVC controller/routing to see these as either two different methods on the controller or a method signature where the complex object is optional?
Either two POST methods (this creates ambiguity exception)
public async Task<IActionResult> Index(){}
public async Task<IActionResult> Index([FromBody] ComplexObject co){}
or complexObject is optional (if not it throws a Executing ObjectResult, writing value of type '"Microsoft.AspNetCore.Mvc.SerializableError" on the subscription creation step.)
public async Task<IActionResult> Index([FromBody] ComplexObject co){}
Another way around this :
public class AllowBindToNullAttribute : ModelBinderAttribute
{
public AllowBindToNullAttribute()
: base(typeof(AllowBindToNullBinder))
{
}
public class AllowBindToNullBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var stream = bindingContext.HttpContext.Request.Body;
string body;
using (var reader = new StreamReader(stream))
{
body = await reader.ReadToEndAsync();
}
var instance = JsonConvert.DeserializeObject(body, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(instance);
}
}
}
You'd use it like this:
public async Task<IActionResult> Index(
[FromBody] [AllowBindToNull] ComplexObject co = null){}
I used the empty parameter method signature and checked the body for data. Not ideal.

Writing to a HttpResponse with BufferOutput=false blocks when an ActionResult comes from a ASP.NET MVC 4 asynchronous action and Glimpse is enabled

I have a custom ActionResult that sets HttpResponse.BufferOutput = false and then writes data to the response stream. I noticed that when the action result comes from a task-based asynchronous action method in ASP.NET MVC, writing to the response stream blocks. This happens only when Glimpse plugin is enabled in web.config. Glimpse is very useful to me, I really want to have it enabled, at least during development and testing. BufferOutput property should remain false, because the content length can be quite large and I don't want to buffer it in memory.
This is the shortest code that could reproduce this exact behaviour:
public sealed class CustomResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
var resp = context.HttpContext.Response;
resp.BufferOutput = false;
resp.ContentType = "text/plain";
resp.Output.Write(DateTime.UtcNow.ToString());
resp.Flush();
}
}
public sealed class DownloadController : Controller
{
// the client nevers gets the response from this action
public async Task<ActionResult> Async()
{
await Task.Yield();
return new CustomResult();
}
// this works
public ActionResult Sync()
{
return new CustomResult();
}
}
I tested this with the latest Glimpse.Mvc4 package (version 1.3.2).
Am I doing something wrong, is there a workaround to this issue or is this a Glimpse bug and I should report it?

How to force ASP.NET Web API to always return JSON?

ASP.NET Web API does content negotiation by default - will return XML or JSON or other type based on the Accept header. I don't need / want this, is there a way (like an attribute or something) to tell Web API to always return JSON?
Clear all formatters and add Json formatter back.
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
EDIT
I added it to Global.asax inside Application_Start().
Supporting only JSON in ASP.NET Web API – THE RIGHT WAY
Replace IContentNegotiator with JsonContentNegotiator:
var jsonFormatter = new JsonMediaTypeFormatter();
//optional: set serializer settings here
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
JsonContentNegotiator implementation:
public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}
public ContentNegotiationResult Negotiate(
Type type,
HttpRequestMessage request,
IEnumerable<MediaTypeFormatter> formatters)
{
return new ContentNegotiationResult(
_jsonFormatter,
new MediaTypeHeaderValue("application/json"));
}
}
Philip W had the right answer but for clarity and a complete working solution, edit your Global.asax.cs file to look like this: (Notice I had to add the reference System.Net.Http.Formatting to the stock generated file)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace BoomInteractive.TrainerCentral.Server {
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication {
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Force JSON responses on all requests
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
}
}
}
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
This clears the XML formatter and thus defaulting to JSON format.
Inspired by Dmitry Pavlov's excellent answer, I altered it slightly so I could plug-in whatever formatter I wanted to enforce.
Credit to Dmitry.
/// <summary>
/// A ContentNegotiator implementation that does not negotiate. Inspired by the film Taken.
/// </summary>
internal sealed class LiamNeesonContentNegotiator : IContentNegotiator
{
private readonly MediaTypeFormatter _formatter;
private readonly string _mimeTypeId;
public LiamNeesonContentNegotiator(MediaTypeFormatter formatter, string mimeTypeId)
{
if (formatter == null)
throw new ArgumentNullException("formatter");
if (String.IsNullOrWhiteSpace(mimeTypeId))
throw new ArgumentException("Mime type identifier string is null or whitespace.");
_formatter = formatter;
_mimeTypeId = mimeTypeId.Trim();
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
return new ContentNegotiationResult(_formatter, new MediaTypeHeaderValue(_mimeTypeId));
}
}
This has correct headers set. Seems a bit more elegant.
public JsonResult<string> TestMethod()
{
return Json("your string or object");
}
If you want to do that for one method only, then declare your method as returning HttpResponseMessage instead of IEnumerable<Whatever> and do:
public HttpResponseMessage GetAllWhatever()
{
return Request.CreateResponse(HttpStatusCode.OK, new List<Whatever>(), Configuration.Formatters.JsonFormatter);
}
this code is pain for unit testing but that's also possible like this:
sut = new WhateverController() { Configuration = new HttpConfiguration() };
sut.Configuration.Formatters.Add(new Mock<JsonMediaTypeFormatter>().Object);
sut.Request = new HttpRequestMessage();
for those using OWIN
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
becomes (in Startup.cs):
public void Configuration(IAppBuilder app)
{
OwinConfiguration = new HttpConfiguration();
ConfigureOAuth(app);
OwinConfiguration.Formatters.Clear();
OwinConfiguration.Formatters.Add(new DynamicJsonMediaTypeFormatter());
[...]
}
Yo can use in WebApiConfig.cs:
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
public System.Web.Http.Results.JsonResult<MeineObjekt> Get()
{
return Json(new MeineObjekt()
{
Cod = "C4666",
Payment = 10.0m,
isEnough = false
});
}

Resources