I have an Azure Durable function (.NET 6) triggered with httpTrigger, I'm trying to prevent start of the function based on the parameters received in the http request.
The code I tried so far :
[FunctionName(nameof(StartOrchestrator))]
public async Task<HttpResponseMessage> StartOrchestrator(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter)
{
var stringContent = await req.Content.ReadAsStringAsync();
var parameter = GetParam(stringContent);
if (string.IsNullOrEmpty(filter.MyParam))
{
//Here I want to prevent start of the orchestration with a 400 bad request error
return req.CreateErrorResponse(HttpStatusCode.BadRequest, "Request doesn't contains MyParam");
}
var instanceId = await starter.StartNewAsync(nameof(RunOrchestrator), parameter);
return starter.CreateCheckStatusResponse(req, instanceId);
}
The result I'm getting :
Is there a way to do what I'm trying to ?
It should be a semaphore issue, two threads are created right here and azure functions doesn't support synchronouse operations so this is where the 500 is coming from.
The solution is to set the variable FUNCTIONS_V2_COMPATIBILITY_MODE
Related
I have a client using HttpClient.GetAsync to call into a Azure Function Http Trigger in .Net 5.
When I call the function using PostMan, I get my custom header data.
However, when I try to access my response object (HttpResponseMessage) that is returned from HttpClient.GetAsync, my header data empty.
I have my Content data and my Status Code. But my custom header data are missing.
Any insight would be appreciated since I have looking at this for hours.
Thanks for you help.
Edit: Here is the code where I am making the http call:
public async Task<HttpResponseMessage> GetQuotesAsync(int? pageNo, int? pageSize, string searchText)
{
var requestUri = $"{RequestUri.Quotes}?pageNo={pageNo}&pageSize={pageSize}&searchText={searchText}";
return await _httpClient.GetAsync(requestUri);
}
Edit 8/8/2021: See my comment below. The issue has something to do with using Blazor Wasm Client.
For anyone having problems after following the tips on this page, go back and check the configuration in the host.json file. you need the Access-Control-Expose-Headers set to * or they won't be send even if you add them. Note: I added the "extensions" node below and removed my logging settings for clarity.
host.json (sample file):
{
"version": "2.0",
"extensions": {
"http": {
"customHeaders": {
"Access-Control-Expose-Headers": "*"
}
}
}
}
This is because HttpResponseMessage's Headers property data type is HttpResponseHeaders but HttpResponseData's Headers property data type is HttpHeadersCollection. Since, they are different, HttpResponseHeaders could not bind to HttpHeadersCollection while calling HttpClient.GetAsync(as it returns HttpResponseMessage).
I could not find a way to read HttpHeadersCollection through HttpClient.
As long as your Azure function code is emitting the header value, you should be able to read that in your client code from the Headers collection of HttpResponseMessage. Nothing in your azure function (which is your remote endpoint you are calling) makes it any different. Remember, your client code has no idea how your remote endpoint is implemented. Today it is azure functions, tomorrow it may be a full blown aspnet core web api or a REST endpoint written in Node.js. Your client code does not care. All it cares is whether the Http response it received has your expected header.
Asumming you have an azure function like this where you are adding a header called total-count to the response.
[Function("quotes")]
public static async Task<HttpResponseData> RunAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("Quotes");
logger.LogInformation("C# HTTP trigger function processed a request for Quotes.");
var quotes = new List<Quote>
{
new Quote { Text = "Hello", ViewCount = 100},
new Quote { Text = "Azure Functions", ViewCount = 200}
};
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("total-count", quotes.Count.ToString());
await response.WriteAsJsonAsync(quotes);
return response;
}
Your existing client code should work as long as you read the Headers property.
public static async Task<HttpResponseMessage> GetQuotesAsync()
{
var requestUri = "https://shkr-playground.azurewebsites.net/api/quotes";
return await _httpClient.GetAsync(requestUri);
}
Now your GetQuotesAsync method can be called somewhere else where you will use the return value of it (HttpResponseMessage instance) and read the headers. In the below example, I am reading that value and adding to a string variable. HttpResponseMessage implements IDisposable. So I am using a using construct to implicitly call the Dispose method.
var msg = "Total count from response headers:";
using (var httpResponseMsg = await GetQuotesAsync())
{
if (httpResponseMsg.Headers.TryGetValues("total-count", out var values))
{
msg += values.FirstOrDefault();
}
}
// TODO: use "msg" variable as needed.
The types which Azure function uses for dealing with response headers is more of an implementation concern of azure functions. It has no impact on your client code where you are using HttpClient and HttpResponseMessage. Your client code is simply dealing with standard http call response (response headers and body)
The issue is not with Blazor WASM, rather if that header has been exposed on your API Side. In your azure function, add the following -
Note: Postman will still show the headers even if you don't expose the headers like below. That's because, Postman doesn't care about CORS headers. CORS is just a browser concept and not a strong security mechanism. It allows you to restrict which other web apps may use your backend resources and that's all.
First create a Startup File to inject the HttpContextAccessor
Package Required: Microsoft.Azure.Functions.Extensions
[assembly: FunctionsStartup(typeof(FuncAppName.Startup))]
namespace FuncAppName
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddScoped<HttpContextAccessor>();
}
}
}
Next, inject it into your main Function -
using Microsoft.AspNetCore.Http;
namespace FuncAppName
{
public class SomeFunction
{
private readonly HttpContext _httpContext;
public SomeFunction(HttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
[FunctionName("SomeFunc")]
public override Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, new[] { "post" }, Route = "run")] HttpRequest req)
{
var response = "Some Response"
_httpContext.Response.Headers.Add("my-custom-header", "some-custom-value");
_httpContext.Response.Headers.Add("my-other-header", "some-other-value");
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "my-custom-header, my-other-header");
return new OkObjectResult(response)
}
If you want to allow all headers you can use wildcard (I think, not tested) -
_httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "*");
You still need to add your web-app url to the azure platform CORS. You can add * wildcard, more info here - https://iotespresso.com/allowing-all-cross-origin-requests-azure-functions/
to enable CORS for Local Apps during development - https://stackoverflow.com/a/60109518/9276081
Now to access those headers in your Blazor WASM, as an e.g. you can -
protected override async Task OnInitializedAsync()
{
var content = JsonContent.Create(new { query = "" });
using (var client = new HttpClient())
{
var result = await client.PostAsync("https://func-app-name.azurewebsites.net/api/run", content);
var headers = result.Headers.ToList();
}
}
I have office 365 trigger "when new e-mail arrives?"
i initialize a variable username with value Max Sample
Then called azure function FxNet21HttpTrigger1
and if determine there a username for the Logic App is this possible to chnge it there give another Variable back
check the the username and do one thing if it is "Donald Duck" or another thing if not
I'm searching for a minimal way to set value in the azure function and react on the value in the logic app.
Logic App Designer Screenshot
This is using Version 1 Azure functions (version 2 and 3 are similar):
using System.Net;
using System.Net.Http;
....
[FunctionName("MyFunction")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
return new GetHtmlResponseContent("<html><body>Hello <b>world</b></body></html>"};
}
private static HttpResponseMessage GetHtmlResponseContent(string msg, HttpStatusCode httpStatusCode)
{
var response = new HttpResponseMessage(httpStatusCode);
response.Content = new StringContent(msg));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
You might want to return a JSON payload containing your return value(s).
How do I return JSON from an Azure Function
I have a working azure functions app where I added a sub-orchestrator with activity functions inside. The rest of the app is working just fine, but not the added code. From the logs, I can see that the sub-orchestrator is reached, but when it tries to hit the activity function, it throws the error below.
Function 'TestActivity (Orchestrator)' failed with an error. Reason: Microsoft.Azure.WebJobs.FunctionFailedException: The activity function 'GetStringInput' failed: "Unable to resolve function name 'GetStringInput'.". See the function execution logs for additional details. ---> System.InvalidOperationException: Unable to resolve function name 'GetStringInput'. at Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.FunctionInstanceLogger.d__6.MoveNext() in C:\projects\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Diagnostics\FunctionInstanceLogger.cs:line 80
Here's the client orchestrator:
[FunctionName(FUNC_INITIALIZE)]
public static async Task<HttpResponseMessage> InitializeAsync(HttpRequestMessage req,
[OrchestrationClient(TaskHub = "%Name%")]
DurableOrchestrationClientBase client,
ILogger logger)
{
var body = new BodyObject {Something:"inside"};
var instance_id = await client.StartNewAsync(FUNC_RELEASE, body);
return req.CreateResponse(HttpStatusCode.Accepted);
}
Orchestrator:
[FunctionName(FUNC_RELEASE)]
public static async Task<string> Release(
[OrchestrationTrigger] DurableOrchestrationContextBase ctx,
ILogger log)
{
var ctx_obj = ctx.GetInput<BodyObject>();
var response = await ctx.CallSubOrchestratorAsync<int>(FUNC_ORCHESTRATE_MORE, JsonConvert.SerializeObject(ctx_json));
//do stuff with response
return new ResultObject = {Result:"response"};
}
Another Orchestrator:
[FunctionName(FUNC_ORCHESTRATE_MORE)]
public static async Task<string> OrchestrateMore(
[OrchestrationTrigger]
DurableOrchestrationContextBase ctx,
ILogger log)
{
var input = ctx.GetInput<string>();
//do stuff
var inpu_json = JsonConvert.SerilializeObject(input);
var response = await ctx.CallActivityAsync<int>(FUNC_ACTIVITY, input_json);
//do stuff with response
return JsonConvert.SerializeObject(new ResultObject = {Result:"response"});
}
Activity
[FunctionName(FUNC_ACTIVITY)]
public static async Task<string> DoingActivity(
[ActivityTrigger] string input,
ILogger log)
{
//do stuff with input
return string_of_info;
}
From the portal, I can see that the activity functions exist and are enabled. Changing the signature of the activity functions to use the type DurableActivityContext yielded the same results. Submitting the request from inside the portal itself yielded a 404 not found error only on the activities added, not on existing activities.
Other info:
.NET 472,
Microsoft.Azure.WebJobs v2.3.0,
Microsoft.Azure.WebJobs.Http v1.2.0,
Microsoft.Azure.WebJobs.Extensions.DurableTask v1.8.4
Microsoft.Azure.WebJobs.ServiceBus v2.2.0
Microsoft.NET.Sdk.Functions v1.0.29
App is being deployed through Azure DevOps.
Any help would be much appreciated!
I am working on an azure function V2 that peeks the messages from the service bus.
In this function I would like to know the state of the messages (Active, Deferred, Scheduled)
I know that Microsoft.ServiceBus.Messaging.BrokeredMessage has a State property.
But I am using Microsoft.Azure.ServiceBus. So How do I get the state of Microsoft.Azure.ServiceBus.Message?
Just an example function that explains my need:
[FunctionName("GetStates")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = "getstates")] HttpRequest req, ILogger log)
{
var result = new Dictionary<string, int>() /** string: state, int: counter **/
receiver = new MessageReceiver("MyConnectionString", EntityNameHelper.FormatSubscriptionPath("MyTopic", "MySubscription"));
var messages = await receiver.PeekBySequenceNumberAsync(receiver.LastPeekedSequenceNumber, 50);
// How to get the states???
return new OkObjectResult(result);
}
When the BrokeredMessage type from track 0 SDK was moved to the Message type in track 1 (Microsoft.Azure.ServiceBus), the MessageState was not implemented. While I don't know the rationale behind that decision or wherever this was just missed out by mistake. You could raise an issue with the Azure SDK using the Azure SDK repository issue tracker here.
I'm a little new to ASP.Net and Asynchronous coding so bear with me. I have written an asynchronous wrapper in C# for a web API that I would like to use in a ASP.Net application.
Here is one of the functions in the C# API wrapper:
public async Task<string> getProducts()
{
Products products = new Products();
products.data = new List<Item>();
string URL = client.BaseAddress + "/catalog/products";
string additionalQuery = "include=images";
HttpResponseMessage response = await client.GetAsync(URL + "?" + additionalQuery);
if (response.IsSuccessStatusCode)
{
Products p = await response.Content.ReadAsAsync<Products>();
products.data.AddRange(p.data);
while (response.IsSuccessStatusCode && p.meta.pagination.links.next != null)
{
response = await client.GetAsync(URL + p.meta.pagination.links.next + "&" + additionalQuery);
if (response.IsSuccessStatusCode)
{
p = await response.Content.ReadAsAsync<Products>();
products.data.AddRange(p.data);
}
}
}
return JsonConvert.SerializeObject(products, Formatting.Indented);
}
I then have a WebMethod in my ASP.Net application (which will be called using Ajax from a Javascript file) which should call the getProducts() function.
[WebMethod]
public static string GetProducts()
{
BigCommerceAPI api = getAPI();
return await api.getProducts();
}
Now of course this will not work as the WebMethod is not an async method. I have tried to change it to an async method which looked like:
[WebMethod]
public static async Task<string> GetProducts()
{
BigCommerceAPI api = getAPI();
return await api.getProducts();
}
This code does run, but as soon as it gets to the HttpResponseMessage response = await client.GetAsync(URL + "?" + additionalQuery); line in the getProducts() function the debugger will stop without any errors or data being returned.
What am I missing? How can I get call this asynchronous API from my ASP application?
So I actually resolved an issue very similar to this last night. It's odd because the call worked in .net 4.5. But we moved to 4.5.2 and the method started deadlocking.
I found these enlightening articles (here, here, and here) on async and asp.net.
So I modified my code to this
public async Task<Member> GetMemberByOrganizationId(string organizationId)
{
var task =
await
// ReSharper disable once UseStringInterpolation
_httpClient.GetAsync(string.Format("mdm/rest/api/members/member?accountId={0}", organizationId)).ConfigureAwait(false);
task.EnsureSuccessStatusCode();
var payload = task.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Member>(await payload.ConfigureAwait(false),
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
}
which resolved my deadlocking issue.
So TLDR: from the Stephen Cleary article
In the overview, I mentioned that when you await a built-in awaitable,
then the awaitable will capture the current “context” and later apply
it to the remainder of the async method. What exactly is that
“context”?
Simple answer:
If you’re on a UI thread, then it’s a UI context. If you’re responding
to an ASP.NET request, then it’s an ASP.NET request context.
Otherwise, it’s usually a thread pool context. Complex answer:
If SynchronizationContext.Current is not null, then it’s the current
SynchronizationContext. (UI and ASP.NET request contexts are
SynchronizationContext contexts). Otherwise, it’s the current
TaskScheduler (TaskScheduler.Default is the thread pool context).
and the solution
In this case, you want to tell the awaiter to not capture the current
context by calling ConfigureAwait and passing false
I am not sure what is [WebMethod] in ASP.NET. I remember it used to be SOAP web services but no one does it anymore as we have Web API with controllers where you can use async/await in action methods.
One way to test your code would be to execute async method synchronously using .Result:
[WebMethod]
public static string GetProducts()
{
BigCommerceAPI api = getAPI();
return api.getProducts().Result;
}
As maccettura pointed out in the comment, it's a synchronous call and it locks the thread. To make sure you don't have dead locks, follow Fran's advice and add .ConfigureAwait(false) at the end of each async call in getProducts() method.
First by convention GetProducts() should be named GetProductsAsync().
Second, async does not magically allocate a new thread for it's method invocation. async-await is mainly about taking advantage of naturally asynchronous APIs, such as a network call to a database or a remote web-service.
When you use Task.Run, you explicitly use a thread-pool thread to execute your delegate.
[WebMethod]
public static string GetProductsAsync()
{
BigCommerceAPI api = getAPI();
return Task.Run(() => api.getProductsAsync().Result);
}
Check this link It's a project sample about how to implement Asynchronous web services call in ASP.NET
I had a very similar issue:
Main webapp is a ASP.NET 4.5 Web forms, but many of its functions implemented as AJAX calls from UI to a [webMethod] decorated function in the aspx.cs code-behind:
The webmethod makes an async call to a proxy. This call was
originally implemented with Task.Run() and I tried to rewrite with
just await ...
[WebMethod]
public static async Task<OperationResponse<CandidatesContainer>> GetCandidates(string currentRoleName, string customerNameFilter, string countryFilter, string currentQuarter)
{
string htmlResult = String.Empty;
List<CandidateEntryDTO> entries = new List<CandidateEntryDTO>();
try
{
entries = await GetCandiatesFromProxy(currentUser, currentRoleName, customerNameFilter, countryFilter, currentQuarter)
.ConfigureAwait(false);
}
catch (Exception ex)
{
log.Error("Error .....", ex);
}
CandidatesContainer payloadContainer = new CandidatesContainer {
CountryMappedCandiates = ...,
GridsHtml = htmlResult };
return new OperationResponse<CandidatesContainer>(payloadContainer, true);
}
3) The call GetCandiatesFromProxy(...) is the top of a chain of several async methods and at the bottom there's finally a HttpClient.GetAsync(...) call:
private async Task<B2PSResponse<string>> GetResponseFromB2PService(string serviceURI)
{
string jsonResultString = String.Empty;
if (_httpClientHandler == null)
{
_httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
}
if (_client == null)
{
_client = new HttpClient(_httpClientHandler);
}
HttpResponseMessage response = await _client.GetAsync(serviceURI).ConfigureAwait(false);
HttpContent content = response.Content;
string json = String.Empty;
if (response.StatusCode == HttpStatusCode.OK)
{
json = await content.ReadAsStringAsync().ConfigureAwait(false);
}
B2PSResponse<string> b2psResponse = new B2PSResponse<string>(response.StatusCode, response.ReasonPhrase, json);
return b2psResponse;
}
The code was not working (was stuck on the lowest level await) until
I started to add .ConfigureAwait(false) to each await call.
Interesting, that I had to add these .ConfigureAwait(false) to all await calls on the chain - all the way to the top call in the webMethod. Removing any of them would break the code - it would hang after the await that does not have the .ConfigureAwait(false).
The last point: I had to modify the Ajax call's SUCCESS path. The default Jason serialization for webmethods makes the result sent to AJAX call as
{data.d.MyObject}
i.e. inserts the {d} field containing the actual payload. After the webmethod return value was changed from MyObject to Task - this no longer worked - my payload was not found in the {data.d}. The result now contains
{data.d.Result.MyObject}
This is simply the result of serializing the Task object - which has the .Result field.
With one small change to the AJAX call is now working.