I call your experience to help me solve a problem.
I have this code:
public class FanController : ApiController
{
[ActionName("ImportAwb")]
[HttpPost]
public async Task<object> ImportAwb([FromBody]JObject data)
{
try
{
string username = data["username"].ToString();
string clientId = data["clientId"].ToString();
string userPass = data["userPass"].ToString();
string fisier = data["fisier"].ToString();
var responseString = await FanCourier.ImportAwbIntegrat(username, clientId, userPass, fisier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
If i left just one method like this, i can call it from Postman with no problem, but if i try to make another one, like this:
{
public class FanController : ApiController
{
[ActionName("ImportAwb")]
[HttpPost]
public async Task<object> ImportAwb([FromBody]JObject data)
{
try
{
string username = data["username"].ToString();
string clientId = data["clientId"].ToString();
string userPass = data["userPass"].ToString();
string fisier = data["fisier"].ToString();
var responseString = await FanCourier.ImportAwbIntegrat(username, clientId, userPass, fisier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
[ActionName("PrintareAwbHtml")]
[HttpPost]
public async Task<object> PrintareAwbHtml([FromBody]FanCourier fanCourier)
{
try
{
var responseString =
await fanCourier.PrintareAwbHtml(fanCourier);
return Ok(responseString);
}
catch (Exception ex)
{
return (ex);
}
}
The response from Postman call is:
"Multiple actions were found that match the request: \r\nImportAwb on type Courier.Rest.Controllers.FanController\r\nPrintareAwbHtml on type Courier.Rest.Controllers.FanController"
I was tried to add a [Route("api/[controller]")] before public class FanController : ApiController and the error was change to:
No action was found on the controller 'Fan' that matches the request.
I have tried to find something on the internet but i found nothing to help my situations.
You should define routes for each of your actions so you know which will be called when specific API is called.
Use [Route] tag to accomplish that
[ActionName("ImportAwb")]
[HttpPost]
[Route("Action1")]
public async Task<object> ImportAwb([FromBody]JObject data)
{
...
And from postman, call your endpoint with url being. http://yoururl.com/Action1.
(YourUrl would be the path you set up for this controller... might include /api or what you might have configured. Add /Action1 to the end of that url)
You can have multiple routes to the same URL as long as they are different methods (post, get, delete, patch etc.).
As I am new to WebAPI I have been experimenting with samples from the web. I have an ApiController based class which handles Post, Get, etc. From my client application, I can perform a Get and a Delete successfully but when I do a Post or a Put of a string, I can see the string value is null at the server and I get StatusCode 204, No Content at the client. Using Postman I can successfully do a Post or Put so it seems to be a problem with my client app.
Have tried basing the client on .Net 4.7.2 as well as .NET Core 2.2
Here is the entire controller class of my WebAPI program:
namespace WebApplication1.Controllers
{
public class ValuesController : ApiController
{
static List<string> strings = new List<string>()
{
"value0", "value1", "Value2"
};
// GET api/values
public IEnumerable<string> Get()
{
return strings;
}
// GET api/values/5
public string Get(int id)
{
return strings[id];
}
// POST api/values
public void Post([FromBody]string value)
{
strings.Add(value);
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
strings[id] = value;
}
// DELETE api/values/5
public void Delete(int id)
{
strings.RemoveAt(id);
}
}
}
Here is code from my client Program:
class Program
{
static HttpClient client = new HttpClient();
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("http://localhost:56037/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
var response = await client.PutAsJsonAsync($"api/values/2", new StringContent("zzz"));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
I set breakpoints in the Put and Post methods but the value of the string being passed is null, not the value I sent to the server.
Id maybe try
var response = await client.PutAsJsonAsync($"api/values/2", new StringContent("zzz"), System.Text.Encoding.UTF8, "application/json");
Turns out that I needed to enclose the string in quotes as follows:
var response = await client.PutAsync("api/values/0", "\"zzz\"", new StringTypeFormatter());
response = await client.PostAsync("api/values", "\"zzz\"", new StringTypeFormatter());
If you want the item at the index, try:
strings.ElementAt(id) = value;
I'm trying to pass a big string in my controller using json. Also i need Controller to send me an answer.
Here is my controller in web api:
public class CustomersController : ApiController
{
// GET: api/Customers
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET: api/Customers/5
public string Get(int id)
{
return "value";
}
// POST: api/Customers
public void Post([FromBody]string value)
{
}
// PUT: api/Customers/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Customers/5
public void Delete(int id)
{
}
}
First of all where i should read my string and where should i send an answer?
And here is my client which try to send a string
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://192.168.1.15:8282/",new StringContent("Mystring", Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
}
I need my web api to read my string, and then to send me an answer
Instead of having the methods as void you need to return the string value from the controller methods.
Also, don't forget to decorate the methods with respective http verb attribubte (HttpGet, HttpPost, HttpPut etc.) which the method is responsible to serve.
Here's an example where the method returns an Ok result, this generate an http status code 200 with the string in the response body
[HttpPost]
public IHttpActionResult Post([FromBody]string value)
{
return Ok(value);
}
Then for the client call.
First of, you need to specify the route to the controller correctly
192.168.1.15:8282/api/Customers
Then, sending a single string as content when using the content-type of application/json is not suitable as json always start parsing from an object {} or array [].
The easiest way of sending a single string is therefore to just change the content type to application/x-www-form-urlencoded and adding a = sign infront of the string
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://192.168.1.15:8282/api/Customers",new StringContent("=Mystring", Encoding.UTF8, "application/x-www-form-urlencoded"));
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
}
}
I studied over the Internet regarding Task Async method but cannot seem to find an approach to assign my return value in Task Async to another object. The first method is to prepare HTTP Request header and Uri.
public static async Task MainAsync()
{
string token = await AuthHelper.AcquireToken(tenantId, clientId, clientSecret);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.BaseAddress = new Uri("https://foo.net");
await GetValue(client);
}
}
The second method is to use GetAsync to call to an API to get the JSON and the two last lines I extract only value from the "Value" field in the JSON body.
public static async Task<String> GetValue(HttpClient client)
{
string url = $"/mykey/key01";
using (var httpResponse = await client.GetAsync(url))
{
httpResponse.EnsureSuccessStatusCode();
string responsContent = await httpResponse.Content.ReadAsStringAsync();
JObject json = JObject.Parse(responsContent);
string value = json["value"].ToString();
return value;
}
}
Now I would like to use this value to assign to another object, but not sure how to do so. I managed to return the valid value. Is it possible to retrieve the value from another method or even different class?
[Updated] The main function is:
static void Main(string[] args)
{
try
{
MainAsync().Wait();
}
catch (Exception e)
{
Console.WriteLine(e.GetBaseException().Message);
}
}
Update
To be more clear. The HTTP response message is a JSON format and I can return the value from Value property in this JSON. Now how I can to reuse the value from an external method or class
I'm not sure exactly what you are trying to achieve. And there would be thorough debates about your architecture, you can do something like this..
Update
Because your MainAsync is static it can be called form anywhere.
You just need to modify it a bit to return your result as follows :
public static async Task<string> MainAsync()
{
...
return await GetValue(client);
...
And somewhere else
public class MyAwesomeClass
{
public async Task DoMagic()
{
var newValueOfSomething = await MainAsync();
// hilarity ensues
}
}
You can Make it more generic and useful, something like below :
Your initial method can be changes to :
public async Task<T> GetContentAsync<T>(HttpClient client)
{
string url = $"/mykey/key01";
using (var httpResponse = await client.GetAsync(url))
{
httpResponse.EnsureSuccessStatusCode();
string responsContent = await httpResponse.Content.ReadAsStringAsync();
return Deserialize<T>(json);
}
}
private T Deserialize<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, SerializationSettings);
}
You can now call method like :
var person = await GetContentAsync<Person>(/*http client*/)
I'm trying to do a simple Azure Function to learn about it. There will be 3 functions:
1 function to insert a row into a table of a database. This table will contain the current date and a string parameters typed by the user and passed by GET.
1 function similar to the previous one, but passing the parameter by POST.
1 function to read the table and show its content.
I've been able to do the first and the third ones. But I can't pass the parameter by POST. I've looked for examples but I couldn't run them with success. The client app is a Windows Forms one.
Could anyone show me an example anout how to pass parameters by POST to the function and how to read them?
Thank's in advance
EDIT:
Here's the code to pass the parameters by GET (this is working fine):
private void button2_Click(object sender, EventArgs e)
{
string cadena = lsql1.Text + "?notas=" + tNotas.Text;
try
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(cadena);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
if (res.StatusCode == HttpStatusCode.OK)
{
MessageBox.Show("Grabado");
}
else
{
MessageBox.Show(res.StatusDescription);
}
}catch (WebException ex)
{
using (Stream s = ex.Response.GetResponseStream())
{
StreamReader sr = new StreamReader(s);
string text = sr.ReadToEnd();
text = text.Substring(1, text.Length - 2);
sr.Close();
text = text.Replace("\\", "");
text = "{" + text + "}";
Error mensajeError = JsonConvert.DeserializeObject<Error>(text);
MessageBox.Show(mensajeError.ExceptionMessage);
}
}
}
And here's the code to receive it and do the insert (this is working too):
[FunctionName("sql1")]
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
log.Info("C# HTTP trigger function processed a request.");
var cnnString = "Server=SERVIDOR;Database=base_prueba;User ID =azure;Password=0000;Trusted_Connection=False;Encrypt=False;";
using (SqlConnection connection = new SqlConnection(cnnString))
{
connection.Open();
SqlCommand cmd = connection.CreateCommand();
DateTime fecha = DateTime.Today;
string notas = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "notas", true) == 0)
.Value;
// insert a log to the database
cmd.CommandText = "INSERT INTO Prueba_Azure (fecha, notas) VALUES ('" + fecha.ToString() + "', '" + notas + "')";
cmd.ExecuteNonQuery();
}
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
return name == req.CreateResponse(HttpStatusCode.OK, "Done");
}
catch (Exception ex)
{
HttpResponseMessage res = req.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
return res;
}
}
What I'm looking for is to to this by POST
In case google took you here, this is how it's done in March 2019 (Azure Functions v3):
public static async void Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
HttpRequest req,
ILogger log)
{
var content = await new StreamReader(req.Body).ReadToEndAsync();
MyClass myClass = JsonConvert.DeserializeObject<MyClass>(content);
}
To get the request content from the request body(post request), you could use req.Content.ReadAsAsync method. Here is the code sample.
Sample request body.
{
"name": "Azure"
}
Define a class to deserialize the post data.
public class PostData
{
public string name { get;set; }
}
Get the post data and display it.
PostData data = await req.Content.ReadAsAsync<PostData>();
log.Info("name:" + data.name);
Client side code to send the post request.
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("function-url");
req.Method = "POST";
req.ContentType = "application/json";
Stream stream = req.GetRequestStream();
string json = "{\"name\": \"Azure\" }";
byte[] buffer = Encoding.UTF8.GetBytes(json);
stream.Write(buffer,0, buffer.Length);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
If you are using System.Text.Json, you can read the POST data in one line:
public static async Task Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
HttpRequest req,
ILogger log)
{
MyClass myClass = await JsonSerializer.DeserializeAsync<MyClass>(req.Body);
}
If you are using Newtonsoft.Json, see the answer by Allen Zhang.
For passing parameters as POST request, you need to do following things:
Make Json model of the parameters that u need to pass,ex:
{"UserProfile":{ "UserId":"xyz1","FirstName":"Tom","LastName":"Hank" }}
Post your data model using client like POSTMAN
Now you will get the posted content in HttpRequestMessage body, sample code is as follows:
[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");
}
}
You can just supply your custom data class as a parameter to the HttpTrigger argument. This way you don't have to mess with the json deserialization yourself:
public async Task<IActionResult> UpdateAccount(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "api/v1/accounts/{id:guid}")]
SomeData someData, // <----- Post body ends up here automatically
HttpRequest req,
Guid id,
ILogger log)
{
log.LogInformation ("Got POST with " + someData.Foo);
}
public class SomeData
{
public string Foo { get; set; } = null!;
}
The query string (name/value pairs) is by default sent in the HTTP message body of a POST request and not as query string. The GetQueryNameValuePairs method will parse the query string and will by default not work with POST request.
For the POST request you could use something similar to this:
var content = request.Content;
string contentInString = content.ReadAsStringAsync().Result;
You need to attach data to the body of the post request and process it properly:
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log) {
// This reads your post request body into variable "data"
string data = await req.Content.ReadAsStringAsync();
// Here you can process json into an object
dynamic parsed = JsonConvert.DeserializeObject(data);
return exitstring == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Something went wrong, sorry")
: req.CreateResponse(HttpStatusCode.OK);
}
You can find a slightly different example here and the exact example here.
It can be done in following way with custom class
Azure Function
[FunctionName("PostParameterFunction")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
try
{
// Convert all request perameter into Json object
var content = req.Content;
string jsonContent = content.ReadAsStringAsync().Result;
dynamic requestPram = JsonConvert.DeserializeObject<RequestModel>(jsonContent);
// Validate the required param
if (string.IsNullOrEmpty(requestPram.FirstName))
{
return req.CreateResponse(HttpStatusCode.OK, "Please enter First Name!");
}
if (string.IsNullOrEmpty(requestPram.LastName))
{
return req.CreateResponse(HttpStatusCode.OK, "Please enter Last Name!");
}
//Create object for partner Model to bind the response on it
RequestModel objRequestModel = new RequestModel();
objRequestModel.FirstName = requestPram.FirstName;
objRequestModel.LastName = requestPram.LastName;
//Return Request Model
return req.CreateResponse(HttpStatusCode.OK, objRequestModel);
}
catch (Exception ex)
{
return req.CreateResponse(HttpStatusCode.OK, "Cannot Create Request! Reason: {0}", string.Format(ex.Message));
}
}
Request Class:
public class RequestModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Request Input:
{
"FirstName": "Kiron",
"LastName":"Test"
}
PostMan Output Example:
I have done a very simple example to get data using POST request in Azure Function App. Please find the following example.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
namespace MyFunctions
{
public static class MyFunctionsOperations
{
[FunctionName("MyFunctionsOperations")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
var headers = req.Headers;
string collection = headers.GetValues("collection").First(); //getting parameter from header
CosmosdbOperation obj = new CosmosdbOperation();
dynamic data = await req.Content.ReadAsAsync<object>(); //getting body content
Boolean response = await obj.MyFunctionExecution(data.ToString(), collection);
return (response)
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a proper argument in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Operation successfully executed..");
}
}
}
I like the WebApi approach of using [FromBody] attribute, so using IBinding I made my own. Now I can just pass in the object.
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
[Binding]
public sealed class FromBodyAttribute : Attribute
{
}
public class FromBodyBinding : IBinding
{
private readonly ILogger logger;
public FromBodyBinding(ILogger logger)
{
this.logger = logger;
}
public Task<IValueProvider> BindAsync(BindingContext context)
{
// Get the HTTP request
var request = context.BindingData["req"] as DefaultHttpRequest;
return Task.FromResult<IValueProvider>(new FromBodyValueProvider(request, logger));
}
public bool FromAttribute => true;
public Task<IValueProvider> BindAsync(object value, ValueBindingContext context)
{
return null;
}
public ParameterDescriptor ToParameterDescriptor() => new ParameterDescriptor();
}
public class FromBodyBindingProvider : IBindingProvider
{
private readonly ILogger logger;
public FromBodyBindingProvider(ILogger logger)
{
this.logger = logger;
}
public Task<IBinding> TryCreateAsync(BindingProviderContext context)
{
IBinding binding = new FromBodyBinding(this.logger);
return Task.FromResult(binding);
}
}
public class FromBodyValueProvider : IValueProvider
{
private HttpRequest request;
private ILogger logger;
public FromBodyValueProvider(HttpRequest request, ILogger logger)
{
this.request = request;
this.logger = logger;
}
public async Task<object> GetValueAsync()
{
try
{
string requestBody = await new StreamReader(this.request.Body).ReadToEndAsync();
object result = JsonConvert.DeserializeObject(requestBody);
return result;
}
catch (System.Exception ex)
{
this.logger.LogCritical(ex, "Error deserializing object from body");
throw ex;
}
}
public Type Type => typeof(object);
public string ToInvokeString() => string.Empty;
}
public class BindingExtensionProvider : IExtensionConfigProvider
{
private readonly ILogger logger;
public BindingExtensionProvider(ILogger<Startup> logger)
{
this.logger = logger;
}
public void Initialize(ExtensionConfigContext context)
{
// Creates a rule that links the attribute to the binding
context.AddBindingRule<FromBodyAttribute>().Bind(new FromBodyBindingProvider(this.logger));
}
}
Then inside your Startup.cs file, add the binding.
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
JsonConvert.DefaultSettings = () =>
{
return new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
},
Formatting = Formatting.Indented
};
};
builder.Services.AddLogging();
builder.AddExtension<BindingExtensionProvider>();
}
}
Now you can just have a regular old class, just like WebApi!
[FunctionName("MyFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
[Binding.FromBody] dynamic data) // or you can change 'dynamic' to some class
{
string username = data?.username;
...
}
Here is the point --> https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp
[FunctionName("LuckyNumber")]
public static async Task<IActionResult> Run(
[HttpTrigger(
AuthorizationLevel.Function,
"get", "post",
Route = "max/{max:int?}/min/{min:int?}")] HttpRequest req,
int? max, <-- Parameter max
int? min, <-- Parameter min
ILogger log)
{
int? maxInternal = max;
int? minInternal = min;
}
PS: I´m using .NET 6
We can do it by just one line code using System.Text.Json.
public static async void Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
HttpRequest req,
ILogger log)
{
MyClass myClass = await JsonSerializer.DeserializeAsync<MyClass>(req.Body);
}