[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var data = new { key = key, id = req.Query["mailid"] };
var result = await myHttpClient.PostAsJsonAsync("https://someapi.com/api/info", data);
return new OkObjectResult(result);
}
Hi.
Im trying to build an azure function, which gets the variable mailid and sends a POST request to an API with the apikey and the mailID in a body, so it can check the status of the mail and give back if its delivered or not. I dont get it to work and "result" returns a json of the httpClient, with the error code 500. Why doesnt the request work? The API expects the key and id in a JSON format in the POST-body.
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string mailid = req.Query["mailid"];
var data = new Dictionary<string, string> {
{ "key", key },
{ "id", mailid },
};
//var data = new { key = key, id = req.Query["mailid"] };
var json = JsonConvert.SerializeObject(data, Formatting.Indented);
var stringContent = new StringContent(json);
myHttpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var result = await myHttpClient.PostAsync("https://someapi.com/api/info", stringContent);
var responseString = await result.Content.ReadAsStringAsync();
return new OkObjectResult(responseString);
}
I just got it to work without an anonymous type variable, which the senior advised me to use. This gets me the result I wanted. It seems like it didnt get send in the proper post-body, as a proper json.
Related
I need to save my HTTP message to the blob. I am using the below code. the file is getting created but without content. I need the XML data to be stored in the blob
[FunctionName("Function11")
public static async Task <IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
[Blob("Order/sales_{Datetimenow}.xml", FileAccess.Write, Connection = "AzureWebJobsStorage")] Stream outputStream)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
return new OkObjectResult(requestBody);
}
Looking at the code you posted, and explicitly this part
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
return new OkObjectResult(requestBody);
it shows that you are...
reading the body of the POST
putting the body in the requestBody variable
returning an OkObjectResult with requestBody as the content
What you are NOT doing is writing the body to the outputStream. You should either create a StreamWriter and write to the stream, otherwise there's nothing to be written to the stream.
Thank you.
[FunctionName("HTTPTrigger")]
public static async Task RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
[Blob("sample/order{DateTime.now}.xml", FileAccess.Write, Connection = "AzureWebJobsStorage")] Stream oStream, ILogger log)
{
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
oStream.Write(Encoding.UTF8.GetBytes(requestBody));
return new OkObjectResult(requestBody);
I have a POST function like the following pattern:
[Function("save")]
public async Task<HttpResponseData?> SaveAppAsync([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/save")] HttpRequestData req) { ... }
The following method tries to deserialize the received object to the target model but it keeps failing and I guess it's because the received JSON is improperly received:
protected async Task<T?> InstantiateAsync<T>(HttpRequestData requestData)
{
try
{
var body = await new StreamReader(requestData.Body).ReadToEndAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(body);
}
catch (Exception ex)
{
_logger?.LogError(ex, ex.Message);
}
return default;
}
This is an example of how the "body" string variable looks like:
"{\n \"id\": \"-1\",\n \"name\": \"MyWebApp\",\n \"comments\": \"My web app comments\",\n \"tenantId\": \"1224\",\n \"components\": [\n {\n \"id\": \"-1\",\n \"name\": \"Component1\",\n \"comments\": \"Some comments for this component\"\n }\n ]\n}"
I guess the reason for deserialization failure is the escape characters of "" but I could not find a solution for this problem.
My question is that whether I am missing a certain configuration in the Azure Function's middleware despite I am using the proper one per the following code snippet:
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(app =>
{
app.UseNewtonsoft();
})
Or should I somehow come up with a solution to fix that string? If so, how? Replacing those escape characters with string.Empty does not help.
I was able to deserialize the string using the JsonConvert.DeserializeObject function.
we can use dynamic data type which will represent the data in the Json string.
dynamic type = JsonConvert.DeserializeObject(name);
name is the string from the request object.
Now here I am sending the following string to function:
{\n \"id\": \"-1\",\n \"name\": \"MyWebApp\",\n \"comments\": \"My web app comments\",\n \"tenantId\": \"1224\"}
and I am returning only value of comments and name tag in response.
Entire function code:
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
// code to fetch the string from the req
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
// Actual logic of deserialization
dynamic type = JsonConvert.DeserializeObject(name);
// here I am creating a string and appending the "name" and "comment" string from the deserialized object
string responseMessage = type.name+" "+type.comments;
return new OkObjectResult(responseMessage);
}
I want to add a second answer as in the question it is used HttpRequestData whereas HttpRequest is used in the other answer. The first type is typically used in Isolated-Process functions and the second in In-Process functions.
In such case the easier and straightforward solution:
var model = await request.ReadFromJsonAsync<YourModelType>();
Ref here
Hey I'm using a ServiceBusTrigger azure function to get the messages received in a queue, and then send them to my webapi which is going to do some stuff with that content
[FunctionName("MyAzureFunction")]
public async void Run(
[ServiceBusTrigger("<MyQueue>", Connection = "<MyConnectionString>")] Message myQueueItem, ILogger log)
{
log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem.ToString()}");
var client = new HttpClient();
// Retrieving the string content from the message
var bodyMessage = Encoding.UTF8.GetString(myQueueItem.Body);
// Calling my API to do something based on the message content
var response = await client.PostAsync("<MyAPIUrl>", new StringContent(bodyMessage, Encoding.UTF8, "application/json"));
// doing something based on the response
}
I've been reading about azure functions and in order to it gets cheaper I read about durable functions, I'm looking forward how to use them so I can take decisions based on my response and I can get it working with this ServiceBusTrigger
Needs to be changed the current ServiceBusTrigger function so it calls another Function that will actually do the job:
[FunctionName("MyAzureFunction")] public async void Run(
[ServiceBusTrigger("<MyQueue>", Connection = "<MyConnectionString>")] Message myQueueItem,
[DurableClient] IDurableOrchestrationClient orchestratorClient,
ILogger log) {
log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem.ToString()}");
// Here is where you need to specify in the first parameter the name of the function to be called
// and the last parameter are the params you'll send to that one
var instanceId = await orchestratorClient.StartNewAsync("MyPostFunction", null, myQueueItem);
log.LogInformation($"C# ServiceBus queue trigger function created an async instance of 'MyPostFunction' with the ID: {instanceId}");
}
Then is needed to create another function that will be OrchestrationTrigger type, that will look like this:
[FunctionName("MyPostFunction")] public async void RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context) {
// using the context can be retrieved the parammeters passed in the function above
// in this case I just specify the type of that one and that's it
var myQueueItem = context.GetInput<Message>();
var bodyMessage = Encoding.UTF8.GetString(myQueueItem.Body);
// Create a URI of your API url
var postUri = new Uri($"<MyAPIUrl>");
// depending on your WebAPI you'll need to specify the content type in the headers
var headers = new Dictionary<string, StringValues>() { { "Content-Type", "application/json" } };
// creating durable http request
var request = new DurableHttpRequest(HttpMethod.Post, postUri, headers, bodyMessage);
// Doing the http call async, in this context you'll save money since your function will not be completely waiting for a response
// this one will keep just checking to see if there's a response available or not
var response = await context.CallHttpAsync(request);
// do your stuffs depending in the response
}
In my case I had to specicify the headers in the request, otherwise I used to get 415 Unsupported Media Type can be done in that way, or just creating the request without specifying any header at the begining and then adding those like this:
var request = new DurableHttpRequest(HttpMethod.Post, postUri, null, bodyMessage);
request.Headers.Add("Content-Type", "application/json");
Both options work
my c# code to call webservice -
var content = new StringContent(req.Body.ToString(), Encoding.UTF8, "application/xml"); ;
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://mydemo.com/service.asmx?pk=listCustomer");
var byteArray = Encoding.ASCII.GetBytes("username:password");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
request.Content = content;
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
HttpResponseMessage response = await httpClient.SendAsync(request);
// getting 500 error in response data at root level invalid
in postman i call this azure function to pass xml input.
xml input format is -
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<listCustomer xmlns="http://tempuri.org/">
<id>KH001</id>
<fromDate>01/01/2018</fromDate>
<toDate>01/01/2020</toDate>
</listCustomer>
</soap:Body>
</soap:Envelope>
I test function on the local with postman, below is my code. Maybe you could have a try.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
return new ContentResult { Content = requestBody, ContentType = "application/xml" };
}
And the content type would be right.
How can I mock HttpRequestMessage, specifically the CreateResponse?
var requestMessage = Substitute.For<HttpRequestMessage>();
requestMessage.CreateResponse().ReturnsForAnyArgs(
new HttpResponseMessage(HttpStatusCode.OK));
but I get the exception ...
NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException:
'Could not find a call to return from.
I've seen the questions ... How to mock the CreateResponse<T> extension method on HttpRequestMessage
And associated ... ASP.NET WebApi unit testing with Request.CreateResponse ...
But they don't seem to actually end up mocking the CreateResponse
Additional comments:
I'm trying to write a unit test around the starter of an Azure precompiled C# function ...
[FunctionName("Version")]
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequestMessage req,
TraceWriter log)
{
log.Info("Version function processed a request ... ");
return req.CreateResponse(HttpStatusCode.OK, "Version 0.0.1");
}
and the actual test, where I want to mock up the HttpRequestMessage, specifically the CreateReponse where I get the error is ...
[TestMethod]
public void Version_returns_value()
{
var requestMessage = Substitute.For<HttpRequestMessage>();
requestMessage.CreateResponse(Arg.Any<HttpStatusCode>(), Arg.Any<string>())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
var log = new CustomTraceWriter(TraceLevel.Verbose);
var httpResponseMessage = VersionFunction.Run(requestMessage, log);
var httpContent = httpResponseMessage.Content;
httpContent.Should().Be("Version 0.0.1 :: valid");
}
No need to mock anything here. Everything can be stubbed safely for this test. CreateResponse is an extension method that, internally, makes use of the request's associated HttpConfiguration. That is the only requirements that needs to be setup before using it in your test.
With that, if you update your test as follows, you should be able to properly exercise your test.
[TestMethod]
public async Task Version_returns_value() {
var expected = "\"Version 0.0.1\"";
var config = new HttpConfiguration();
var requestMessage = new HttpRequestMessage();
requestMessage.SetConfiguration(config);
var log = new CustomTraceWriter(TraceLevel.Verbose);
var httpResponseMessage = VersionFunction.Run(requestMessage, null);
var httpContent = httpResponseMessage.Content;
var content = await httpContent.ReadAsStringAsync();
content.Should().Be(expected);
}