I'm writing queue trigger function where I read data from queue, and send them another web service using RESTFul service. Right now, I'm testing a very simple REST api call that I only need to provide token in the header and expect very simple JSON response from the server. The JSON just contains an email address entry and that's about it. My understanding is that if I read response asynchronously, I would need to change function prototype to comply with async call. But that's not possible in Azure function app. So what's the best way to read JSON response object?
This is my attempt so far :
using System;
using System.Net.Http;
using System.Net.Http.Headers;
public static void Run(string myQueueItem, TraceWriter log)
{
string URL = "https://api.spotlightessentials.com/api/v2/user";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("token","<Token value>");
HttpResponseMessage response = client.GetAsync("").Result;
if (response.IsSuccessStatusCode)
{
// How do I read Json response here
}
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
}
In your
if (response.IsSuccessStatusCode)
you can do this:
var responseData = await response.Content.ReadAsAsync<YourObjectTypeHere>();
Or you could also do something like this depending on your needs:
var responseData = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (!string.IsNullOrWhiteSpace(responseData))
{
var responseDataObject =
JsonConvert.DeserializeObject<YourObjectTypeHere>(responseData);
}
Or a combination of parts of the 2.
Related
Hello All I am using Azure's vision analyze api to extract text from my documents,
here is the example code for your reference
//My main function fi.fullfile is the path of my uploaded document
AzureAnalyzeRequest(System.IO.File.ReadAllBytes(fi.FullName));
analyze function
static async void AzureAnalyzeRequest(byte[] byteData)
{
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "MyKey");
// Request parameters
queryString["language"] = "en";
queryString["pages"] = "1,2";
var uri = "https://url-ocr.cognitiveservices.azure.com/vision/v3.2/read/analyze?" + queryString;
HttpResponseMessage response;
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
}
when the above function executed I am getting the error of 400 bad request
but when I tested my api on the below URL
Azure Vision api
it worked fine.
what I am doing wrong here?
According to this MSDOCS the api needs a Json object in the following form:
{
"url":""
}
I think you are passing a byte array, you need a Json object which will contain a URL of the image you want to process.
So here I have created a class called Poco which will host the URL variable.
public class Poco
{
public string url { get; set; }
}
Then I initialized the class and passed the URL then convert that object into a Json object.
Poco p = new Poco();
p.url = "<URL OF YOUR IMAGE>";
string json = JsonConvert.SerializeObject(p);
// Here we are converting the json string to stringcontent which we can pass to httpclient
StringContent data = new StringContent(json, Encoding.UTF8, "application/json");
Now all you have to do is call the Api:
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "<YOURKEY>");
var response = client.PostAsync(url, data);
Console.WriteLine(response.Result.StatusCode);
Console.WriteLine(response.Result);
If you want to use the byte array of image, then I think the content-type header should be application/octet-stream according to this MSDOC
I have a HTTP Listener console app that works on my local machine. When I try to use it inside a HTTP Trigger Azure Function. I always get the 418 error code.
In my console app:
HttpListener listener = new HttpListener();
try
{
listener.Prefixes.Add("http://localhost:11000/");
listener.Start();
} catch (Exception e)
{ // }
do {
var ctx = listener.GetContext();
var res = ctx.Response;
var req = ctx.Request;
var reqUrl = req.Url;
var readStream = new StreamReader(req.InputStream);
var content = readStream.ReadToEnd();
Console.WriteLine(content);
// business logic
readStream.Close();
res.StatusCode = (int)HttpStatusCode.OK;
res.ContentType = "text/plain";
res.OutputStream.Write(new byte[] { }, 0, 0);
res.Close();
if (stopListener) { listener.Stop(); }
} while (listener.IsListening);
Now HTTP Trigger Function uses the HttpRequest class and that seems to give me the 418 error code. I replaced it with HttpListener() but when I add the prefix of the Azure Function string connection (on the CLI), the stream never goes through and its as if its not capturing it? Or what connection should I use? I feel like self-referencing it is the reason its not working.
Azure Function:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpListener listener,
ILogger log,
IBinder binder)
{//same as above}
Is this the right approach to getting data from an external app? So far this has been the way I can see it working via the HTTP Listener.
Any suggestions are welcomed.
Is this the right approach to getting data from an external app?
The right way to access Data from an external source and any other source. You can create an API and use this API to access data from external sources.
For create azure function click hereby Microsoft documents.
Below sample code for access web API in azure function.
var _httpclient = new HttpClient();
var _response = await _httpclient .GetAsync(rul);
var result_= await _response .Content.ReadAsStringAsync();
its use is just like using API in C# code.
Azure Function Code:-
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
namespace _73093902_FunctionApp10
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
var _httpclient = new HttpClient();
var _response = await _httpclient.GetAsync("https://localhost:7101/WeatherForecast");
var result_ = await _response.Content.ReadAsStringAsync();
return new OkObjectResult(result_);
}
}
}
Debug Output:-
I need some help in writing an Azure function with CosmosDB trigger, which will capture some values from cosmosdb like below and create a POST call and trigger an API. Is it possible in Azure function?
Cosmosdb:
The POST API which needs to be passed through Azure function is like this.
Here is the example code to for passing POST in the Azure function
[FunctionName("TestPost")]
public static HttpResponseMessage POST([HttpTrigger(AuthorizationLevel.Function, "put", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
try
{
//create redis connection and database
var RedisConnection = RedisConnectionFactory.GetConnection();
var serializer = new NewtonsoftSerializer();
var cacheClient = new StackExchangeRedisCacheClient(RedisConnection, serializer);
//read json object from request body
var content = req.Content;
string JsonContent = content.ReadAsStringAsync().Result;
var expirytime = DateTime.Now.AddHours(Convert.ToInt16(ConfigurationSettings.AppSettings["ExpiresAt"]));
SessionModel ObjModel = JsonConvert.DeserializeObject<SessionModel>(JsonContent);
bool added = cacheClient.Add("RedisKey", ObjModel, expirytime); //store to cache
return req.CreateResponse(HttpStatusCode.OK, "RedisKey");
}
catch (Exception ex)
{
return req.CreateErrorResponse(HttpStatusCode.InternalServerError, "an error has occured");
}
}
Here are the few examples for Post in Azure functions
Azure function POST
Post Async Function.
I have an Azure Function App with Azure Active Directory configured but when I call if from my client I keep getting an Unauthorized response.
I have tried a couple different scenarios but nothing worked. Below is a snippet of the last bit of code that I tried.
///
var #params2 = new NameValueCollection
{
{"grant_type", "client_credentials"},
{"client_id", $"{ClientId}"},
{"client_secret", $"{ClientSecret}"},
{"username", userId},
{"resource", "https://management.azure.com/"}
};
var queryString2 = HttpUtility.ParseQueryString(string.Empty);
queryString2.Add(#params2);
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"grant_type", "client_credentials"},
{"client_id", ClientId},
{"client_secret", ClientSecret},
{"username", userId}
});
var authorityUri2 = $"{string.Format(CultureInfo.InvariantCulture, AadInstance, Tenant).TrimEnd('/')}/oauth2/token";
//var authorityUri2 = $"https://login.microsoftonline.com/{Tenant}/v2.0/.well-known/openid-configuration";
var authUri2 = String.Format("{0}?{1}", authorityUri2, queryString2);
var client2 = new HttpClient();
var message = client2.PostAsync(authorityUri2, content).Result;
//var message = client2.GetAsync(authorityUri2).Result;
var response = message.Content.ReadAsStringAsync().Result;
dynamic values=null;
try
{
values = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
}
catch
{
values = response;
}
var AuthToken2 = values["access_token"];
client2.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken2);
HttpResponseMessage response2 = await client2.GetAsync(AppBaseAddress.TrimEnd('/') + "/api/AADIntegration");
if (response.IsSuccessStatusCode)
{
// Read the response and data-bind to the GridView to display To Do items.
string s = await response.Content.ReadAsStringAsync();
log.LogInformation($"Success while getting / api / AADIntegration : {s}");
return (ActionResult)new OkObjectResult(s);
}
else
{
string failureDescription = await response.Content.ReadAsStringAsync();
log.LogInformation($"An error occurred while getting / api / AADIntegration : {response.ReasonPhrase}\n {failureDescription}");
return (ActionResult)new OkObjectResult(failureDescription);
}
Data should returned from the Function App.
For client_credentials grant flow your code seems little different. Here I am giving you exact sample for azure function. Just plug and play :))
Example contains:
How would you get token using client_credentials flow
Getting user list From Azure Active Directory tenant using above
token
Access Token Class:
public class AccessTokenClass
{
public string token_type { get; set; }
public string expires_in { get; set; }
public string resource { get; set; }
public string scope { get; set; }
public string access_token { get; set; }
}
Reference To Add:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using System.Collections.Generic;
using System.Net.Http.Headers;
Azure Function Body:
public static class FunctionGetUserList
{
[FunctionName("FunctionGetUserList")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
try
{
log.LogInformation("C# HTTP trigger function processed a request.");
//Token Request endpoint Just replace yourTennantId/Name
string tokenUrl = $"https://login.microsoftonline.com/yourTennantId/Name.onmicrosoft.com/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "b603c7bead87-Your_client_id-e6921e61f925",
["client_secret"] = "Vxf1SluKbgu4P-Your_client_Secret-F0Nf3wE5oGl/2XDSeZ=",
["resource"] = "https://graph.microsoft.com"
});
dynamic json;
AccessTokenClass results = new AccessTokenClass();
HttpClient client = new HttpClient();
var tokenResponse = await client.SendAsync(tokenRequest);
json = await tokenResponse.Content.ReadAsStringAsync();
results = JsonConvert.DeserializeObject<AccessTokenClass>(json);
var accessToken = results.access_token;
//Create Request To Server
using (HttpClient clientNew = new HttpClient())
{
//Pass Token on header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Get Data from API
var requestToAzureEndpoint = await client.GetAsync("https://graph.microsoft.com/v1.0/users");
if (requestToAzureEndpoint.IsSuccessStatusCode)
{
var result_string = await requestToAzureEndpoint.Content.ReadAsStringAsync();
dynamic responseResults = JsonConvert.DeserializeObject<dynamic>(result_string);
return new OkObjectResult(responseResults);
}
else
{
var result_string = await requestToAzureEndpoint.Content.ReadAsStringAsync();
return new OkObjectResult(result_string);
}
}
}
catch (Exception ex)
{
return new OkObjectResult(ex.Message);
}
}
}
Point To Remember
For Azure Active Directory List users access make sure you have following permission:
User.Read.All
Permission Type: Application
You can check here. See the screen shot for better understanding; make sure you have clicked "Grant admin consent for yourTenant" after adding permission.
Note: This is how you can access Azure Active Directory Token using Azure Function after that how to access resource using that token to a specific API endpoint efficiently.
Are you sure you have properly implemented this properly? It looks like a few of your parameters are wrong for the client credential flow. Please double check that you are properly following the client credential flow.
The client credential grant flow is documented here : https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
But for more information on getting this properly working in your function app, please refer to the blog below for more information/help on implementing this.
https://blogs.msdn.microsoft.com/ben/2018/11/07/client-app-calling-azure-function-with-aad/
The value of resource is not correct.
Replace {"resource", "https://management.azure.com/"} with {"resource", $"{ClientId}"}
https://learn.microsoft.com/en-us/azure/azure-functions/functions-manually-run-non-http
I am trying to manual trigger my Azure Timer function App created in 2.0 and developed in .net core 2.0.
When I try to hit the url I get 403 error.
apikey I pass is picked from :
As the article you provided, you need to use _master key under Manage and Host key
I use the following class in my integration tests against service bus triggered Azure Functions.
class AzureFunctionCaller
{
private readonly HttpClient _httpClient;
private readonly string _functionUri;
public AzureFunctionCaller(string functionName)
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("x-functions-key","<Key>");
_functionUri = $"<FUNCTION_ENDPOINT>/admin/functions/{functionName}";
}
public async Task CallViaAdminEndpoint(string content)
{
var httpContent = new StringContent(content, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(_functionUri, httpContent);
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Response content: {responseContent}");
}
}
Then you must send the data in a format where you place the content in "input" object.
var azureFunctionCaller = new AzureFunctionCaller("<FunctionName>");
var obj = new
{
... // properties you want to send
};
var jsonContent = JsonConvert.SerializeObject(new
{
input = JsonConvert.SerializeObject(obj)
});
await azureFunctionCaller.CallViaAdminEndpoint(jsonContent);`
To explain the input property, here is how the same call looks like in postman: