How to consume Azure Fuction from MVC Controller - asp.net-core

I have created and published an Azure Function (HTTP Triggered) for a search functionality. When I type an ID in a search box and click on "Search", it should call the Azure Function and get the result back.
How to integrate the Azure Function with my Controller Action in .NETCore?

Here is the example how you could call your azure function into the controller.
I have a simple azure function which return a name and email once its called. Let's see the below example:
public class InvokeAzureFunctionController : ApiController
{
// GET api/<controller>
public async System.Threading.Tasks.Task<IEnumerable<object>> GetAsync()
{
HttpClient _client = new HttpClient();
HttpRequestMessage newRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost:7071/api/FunctionForController");
HttpResponseMessage response = await _client.SendAsync(newRequest);
dynamic responseResutls = await response.Content.ReadAsAsync<dynamic>();
return responseResutls;
}
}
Test Function For Controller Invocation:
public static class FunctionForController
{
[FunctionName("FunctionForController")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
if (name == null)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
name = data?.name;
}
ContactInformation objContact = new ContactInformation();
objContact.Name = "From Azure Function";
objContact.Email = "fromazure#function.com";
return req.CreateResponse(HttpStatusCode.OK, objContact);
}
}
Simple ContactInformation Class I have Used:
public class ContactInformation
{
public string Name { get; set; }
public string Email { get; set; }
}
PostMan Test:
I have called the controller action from Post Man and its successfully return data from my local azure function through the local controller action. See the screen shot below:
Hope you understand. Just plug and play now.

Related

Could not post value to web API using IFormFile

i have a web application that does an action to upload image to api
the code like this
[HttpPost]
public async Task<IActionResult> Upload([FromForm] UploadModel model)
{
var upload = AccountApi.UploadFile(model);
return Ok("OK");
}
public static object UploadFile(UploadModel model)
{
RestClient client = InitClient();
request.Resource = "Account/UploadFile";
request.Method = Method.POST;
request.AddJsonBody(model);
IRestResponse<object> response = client.Execute<object>(request);
return response.Data;
}
public class UploadModel
{
public long UserId { get; set; }
public string Token { get; set; }
public IFromFile File { get; set; }
}
and there's a web API to handle Rest request above
the code like this
[HttpPost("UploadFile")]
public async Task<object> UploadFileAction(UploadModel model)
{
// the code handle upload file request here
return "Success";
}
my issue is the UploadModel model in web application contains the right value that requested from front-end (UserId = 10, Token = "eyJ..........", File = [object])
but when it posted to API, the 3 properties in UploadModel didn't get the value posted from web application (UserId = 0, Token = null, File = null)
could you help me to find the solution for this. Thanks all
I found the solution myself.
instead of using IFormFile to post, I serialized the image file to a base64 string and post it over API. and in API, I convert it to File object
Thanks for following my question

HttpClient.GetAsync return HttpResponseMessage with null header

net 5.0 lover.
I am new in blazor and .net 5.0, I develop the application with blazor WebAssembly and WebApi.
There are two major Projects: Client, Server.
Client is Blazor WebAssembly and Server is WebApi Controller Project.
In server side, in controller, HttpGet Method, i add a value to Response header:
[HttpGet]
public async Task<ActionResult<IList<Country>>> GetAsync([FromQuery] Pagination paginationDto)
{
/...
httpContext.Response.Headers.Add("TotalPages", totalPages.ToString());
//...
IList<Country> = ...
return result;
}
In Client project razor page, call the api with following method from generic calss:
protected virtual async Task<PaginatedResponse<O>> GetAsync<O>(Pagination pagination)
{
HttpResponseMessage response = null;
try
{
response = await httpClient.GetAsync(RequestUri);
if (response.IsSuccessStatusCode)
{
try
{
//This response Header always is null!
System.Console.WriteLine("response.Headers: " + response.Headers.ToString());
O result = await response.Content.ReadFromJsonAsync<O>();
var paginatedResponse = new PaginatedResponse<O>
{
Response = result,
TotalPages = totalPages
};
return paginatedResponse;
}
//...
return default;
}
When Api call from postman the result and Header is fine and TotalPages is there.
In Client App, the result is ok, but the Header is null.
Any information will save me ;-)
Thanks in Advance.
I think you're overcomplicating this by trying to use headers to pass back a result that can be passed more easily as part of the content. You even sort of realise this you're trying to use a PaginatedResponse in the Blazor client.
So instead of the API returning just a list, have a PaginatedResponse class in a shared library somewhere.. e.g.
/// <summary>
/// Paged result class
/// </summary>
/// <typeparam name="T"></typeparam>
public class PaginatedResponse<T>
{
public int TotalPages { get; set; }
public int Page { get; set; }
public List<T> Data { get; set; }
}
Your API then returns this
[HttpGet]
public async Task<ActionResult<PaginatedResponse<Country>>> GetAsync([FromQuery] Pagination paginationDto)
{
// ... query results here
var result = new PaginatedResponse<Country>()
{
Page = x,
TotalPages = totalPages,
Data = countrylist // from query
};
return result;
}
Your Blazor client can then use the same PaginatedResponse class and just use the standard GetFromJsonAsync method:
var result = await Http.GetFromJsonAsync<PaginatedResponse<Country>>("yourApiUri");
This is why I love Blazor!
This is the exactly answer for how search for answer:
in Server project, in startup.cs, in ConfigureServices method, add following code for CORS or update your CORS rule:
services.AddCors(options => options.AddPolicy(name: "WebApiProjectName or somthing", builder =>
{
builder.WithOrigins("http://localhost:xxxx") //xxxxx is server port
.AllowAnyMethod()
.AllowAnyHeader()
//.AllowCredentials() // its optional for this answer
.WithExposedHeaders("*"); // this is the code you need!
}));

How to change api return result in asp.net core 2.2?

My requirement is when the return type of the action is void or Task, I'd like to return my custom ApiResult instead. I tried the middleware mechanism, but the response I observed has null for both ContentLength and ContentType, while what I want is a json representation of an empty instance of ApiResult.
Where should I make this conversion then?
There are multiple filter in .net core, and you could try Result filters.
For void or Task, it will return EmptyResult in OnResultExecutionAsync.
Try to implement your own ResultFilter like
public class ResponseFilter : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
// do something before the action executes
if (context.Result is EmptyResult)
{
context.Result = new JsonResult(new ApiResult());
}
var resultContext = await next();
// do something after the action executes; resultContext.Result will be set
}
}
public class ApiResult
{
public int Code { get; set; }
public object Result { get; set; }
}
And register it in Startup.cs
services.AddScoped<ResponseFilter>();
services.AddMvc(c =>
{
c.Filters.Add(typeof(ResponseFilter));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
All you have to do is to check the return type and on the basis of the return you can perform whatever operations you want.
Here is the abstract demo:
You have a method:
public Action SomeActionMethod()
{
var obj = new object();
return (Action)obj;
}
Now in your code you can use the following code to get the name of the method:
MethodBase b = p.GetType().GetMethods().FirstOrDefault();
var methodName = ((b as MethodInfo).ReturnType.Name);
Where p in the above code is the class which contains the methods whose return type you want to know.
after having the methodname you can decide on variable methodName what to return.
Hope it helps.

How to create an ASP.NET Core API controller action for HttpPost that can accept a request that has content of type MultipartFormDataContent

I am working on an ASP.NET Core 2.0 API in VS2017.
I want to create a controller action for an HTTP Post method that accept string and byte[] values that I will then use to create records in my SQL database.
From what I understand, if I want to post both string data and a byte[] that represents a file, I have to use MultipartFormDataContent as the type of content in the request from my client.
So, on the API controller action, how is that mapped? Can I have a DTO class in the API that has properties for both the string values and the byte[] value and have it passed into the API controller action via the [FromBody]UploadsDto dto
For example, have a DTO class like this...
public class UploadFileRecordForCreationDto
{
public int LocationId { get; set; }
public string FileName { get; set; }
public byte[] UploadedFile { get; set; }
}
Then have a controller action with this signature...
[HttpPost(Name = "CreateUploadFileRecord")]
public IActionResult CreateUploadFileRecord([FromBody]UploadFileRecordForCreationDto dto)
{
...
...
...
return CreatedAtRoute("GetUploadedFileFile", new { id = linkedResourceToReturn["Id"] }, linkedResourceToReturn);
}
And then have that API action accept a request created using something similar to what I am doing with this test console application on the client side;
static async Task CreateUploadFileRecordAsync()
{
httpClient.BaseAddress = new Uri("https://localhost:44369");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string relativeUrl = "/api/UploadFilesManager";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, relativeUrl);
HttpResponseMessage response = new HttpResponseMessage();
using (var content = new MultipartFormDataContent("--UploadTest"))
{
var values = new[]
{
new KeyValuePair<string,string>("LocationId","1"),
new KeyValuePair<string,string>("FileName","TestFile-01.txt"),
};
foreach (var keyvaluepair in values)
{
content.Add(new StringContent(keyvaluepair.Value, Encoding.UTF8, "application/json"), keyvaluepair.Key);
}
var fileContent = new ByteArrayContent(File.ReadAllBytes(#"C:\testfile-01.txt"));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(DispositionTypeNames.Attachment)
{
Name = "UploadedFile",
FileName = "testfile-01.txt"
};
content.Add(fileContent);
request.Content = content;
response = await httpClient.SendAsync(request);
}
if (response.IsSuccessStatusCode)
{
string result = response.Headers.Location.ToString();
Console.WriteLine("Success:\n");
Console.WriteLine($"New Record Link: [{result}]\n");
}
else
{
Console.WriteLine($"Failed to create new UploadFile record. Error: {0}\n", response.ReasonPhrase);
}
}
If it doesn't just map to a DTO in the FromBody, can anyone provide an example of how to deal with this use case?

How does one determine the route of an Web API 2.2 Action implemented in a base class?

Assume for a moment that I have an abstract controller
public abstract class ResourceController<TResource> : ApiController where TResource: Resource,new()
{
[Route("{id}")]
[HttpGet]
public async Task<IHttpActionResult> FindById([FromUri] string id)
{
TResource resource = null;
// go fetch the resource from a database or something
return Ok(resource)
}
[Route("")]
[HttpPost]
public async Task<IHttpActionResult> Create(TResource resource)
{
TResource resource = null;
// go create the resource or something
return CreatedAtRoute("XXXXX", new { id = resource.Id }, resource);
}
// more methods
}
[RoutePrefix("foo")]
public class FooResourceController : ResourceController<Foo>
{
}
[RoutePrefix("baa")]
public class BaaResourceController : ResourceController<Baa>
{
}
public class Resource
{
public string Id { get; set; }
// some other properties all resources shared
}
At this stage all the actions work, except for creating a new resource. Without overriding the Create method in every subclass, how do I find the correct route of the FindById of the respective controllers from the ResourceController Create method?
For example, if I create a foo resource with id 123 then it would return foo/123. If I created a resource baa with id 456, then it woulds return baa/456.
I'm unable to name the route using attributes, since only one can exist for the application.
Had the same problem. I fixed it by using the Created method in combination with the calling url.
This will only work if yout post doesn't have a dedicated template
My get:
[HttpGet("{id:int}")]
public async Task<IActionResult> GetAsync(int id)
{
try
{
var codetabel = await _reader.GetAsync(id);
var model = Mapper.Map<TCodeTabelModel>(codetabel);
return OkResult(model);
}
catch ....
}
And post:
[HttpPost()]
public async Task<IActionResult> InsertAsync(TCodeTabelModel model)
{
if (!ModelState.IsValid)
return BadRequestResult(ModelState);
try
{
var entity = Mapper.Map<TCodeTabelEntity>(model);
var insertedEntity = await _writer.InsertAsync(entity);
return Created($"{Request.Path.Value}/{insertedEntity.Id}" , Mapper.Map<TCodeTabelModel>(insertedEntity));
}
catch ....
}