sending json and files to Azure Function - api

I am trying to upload files to an Azure Function, but I also need a json object to add addition context and I am stuggling.
I am not sure if its the restClient in vs code that the issue or if its the Azure function not being happy;
POST http://localhost:7071/api/upload
Content-Type: application/json; multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="files"; filename="2020-04-29.png"
Content-Type: image/png
< C:\Users\enend\OneDrive\Pictures\Screenshots\2020-04-29.png
------WebKitFormBoundary7MA4YWxkTrZu0gW--
private async Task<UploadCommand?> GetCommandAsync(HttpRequestData httpRequest)
{
try
{
var command = await httpRequest.ReadFromJsonAsync<UploadCommand>();
if(command == null) return null;
var parser = await MultipartFormDataParser.ParseAsync(httpRequest.Body);
command.Files = parser.Files.ToList();
return command;
}
catch(Exception ex)
{
_log.LogError(ex.ToString());
return null;
}
}
}
public class UploadCommand
{
[JsonPropertyName("files")]
public IEnumerable<FilePart> Files {get;set;} = new List<FilePart>();
[JsonPropertyName("culture")]
public string? Culture { get; set; }
[JsonPropertyName("status")]
public string? Status { get; set; }
[JsonPropertyName("accessType")]
public string? AccessType { get; set; }
[JsonPropertyName("folders")]
public IEnumerable<Folder>? Folders { get; set; }
[JsonPropertyName("fileTypes")]
public IEnumerable<string>? FileTypes { get; set; }
[JsonPropertyName("allowOverrides")]
public bool AllowOverride { get; set; }
}
the above code works, but I can't figure away to add json to it, so my function accepts it (I can send json speratly but not together)
Any thoughts would be appriciated.

Related

how to send array to API which contains image and other data in .net core

When I am passing a single object like below then it is working as per below image
[HttpPost]
public async Task<ActionResult> Post([FromForm] MyModel Details)
{
}
but when I am passing the List of the object to API then it is not working. option to upload a file is not visible. and if I entered any values in the array then also I am getting count 0 for details.
[HttpPost]
public async Task<ActionResult> Post([FromForm] List<MyModel> Details)
{}
I want to pass the List of images and descriptions to API. How can I achieve it?
Thanks in advance!
You need custom model binding for the list model . Here is a similar demo:
custom model binding code:
public class MetadataValueModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (values.Length == 0)
return Task.CompletedTask;
var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options);
bindingContext.Result = ModelBindingResult.Success(deserialized);
return Task.CompletedTask;
}
}
Add the model binder to the model class:
public class MasterDTO
{
public string Comments { get; set; }
public IFormFile File { get; set; }
public List<DetailDTO> Details { get; set; }
public MasterDTO()
{
this.Details = new List<DetailDTO>();
}
}
[ModelBinder(BinderType = typeof(MetadataValueModelBinder))]
public class DetailDTO
{
public Int64 ElementId { get; set; }
public double LowerLimit { get; set; }
public double HigherLimit { get; set; }
public string Status { get; set; }
public string UserAuthorization { get; set; }
public DateTime? AutorizationDate { get; set; }
}
controller/action
[HttpPost]
public async Task<IActionResult> CreateProjectLimit([FromForm] MasterDTO masterDto)
{
//...
return Ok();
}
You can just use postman to pass the list of images and Descriptions to API
Below is the right answer. we can use Postman to pass images in the array as shown below.

Uploading File and Employee Model in POST Web API

I have an Employee Model and Profile Picture. I need to Upload Profile Picture and Model Date both in one POST method. I just need to save Image File Name in Database and Uploading image in a WebRoot Directory.
Here is my Model:
public partial class Employers
{
public int EmployerId { get; set; }
public string Companyname { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public string Password { get; set; }
public string DisplayImage { get; set; }
public bool? IsActive { get; set; }
public DateTime StateDate { get; set; }
}
Here is my Controller Code:
[HttpPost]
public async Task<ActionResult<Employees>> PostEmployees([FromForm] FileUploadAPI Image, [FromBody]Employees employees)
{
try
{
_context.Employees.Add(employees);
await _context.SaveChangesAsync();
await UploadImage(Image, 2, employees.EmployeeId);
var returnInfo = CreatedAtAction("GetEmployees", new { id = employees.EmployeeId }, employees);
return returnInfo;
}
catch(Exception ex)
{
return NoContent();
}
}
public class FileUploadAPI
{
public IFormFile files { get; set; }
}
public async Task<string> UploadImage(FileUploadAPI files, int UserType, int UserId)
{
if (files.files.Length > 0)
{
try
{
if (!Directory.Exists(_hostingEnvironment.WebRootPath + "\\Employees\\"))
{
Directory.CreateDirectory(_hostingEnvironment.WebRootPath + "\\Employees\\");
}
Guid guid = Guid.NewGuid();
string filename = _hostingEnvironment.WebRootPath + "\\Employees\\" + $"EM-{UserType}-UserId-{guid}";
using (FileStream filestream = System.IO.File.Create(filename))
{
await files.files.CopyToAsync(filestream);
filestream.Flush();
return filename;
}
}
catch (Exception ex)
{
return ex.ToString();
}
}
else
{
return "Not Found";
}
}
If i just upload File in POSTMAN without Employee Model, its working fine. But when i pass both File EMployee Data both then FILE is returning null.
Any Suggestion, Solution ?
Thanks
It's impossible to use [FromForm] and [FromBody] simultaneously as is mentioned here. But I think You have 2 choices:
You can either put your JSON body into a form and send Employee data besides the File or use 2 separate endpoints for form upload. An endpoint for uploading user picture using [FromFile] and obtaining a pictureId and another for sending Employee in the body with populated pictureId key.
Firstly change FromBody to FromForm.Then,because you want to save filename to the database,change your code like below:
[HttpPost]
public async Task<ActionResult<Employers>> PostEmployees([FromForm] FileUploadAPI Image, [FromForm]Employers employees)
{
try
{
var filename = await UploadImage(Image, 2, employees.EmployerId);
employees.DisplayImage = filename;
_context.Employers.Add(employees);
await _context.SaveChangesAsync();
var returnInfo = CreatedAtAction("GetEmployees", new { id = employees.EmployerId }, employees);
return returnInfo;
}
catch (Exception ex)
{
return NoContent();
}
}
Your postman should be like below:
Result:

Post JSON to Razor Page, return HTML

After a user fills out a form, I need post the form contents back in JSON and get a chunk of HTML back for display. This seems like a good case for razor pages. The BrandTemplateInfo parameter on the OnPost handler is always null. I can't seem to get the BrandTemplateInfo to populate from the model binder. What am I missing here? This used to be easy with MVC controllers. What am I missing here? Help?
public class PayNowCardModel : PageModel
{
public void OnGet()
{
}
public IActionResult OnPost([FromBody] BrandTemplateInfo brandTemplateInfo)
{
return Page();
}
public BrandTemplateInfo BrandTemplateInfo { get; set; }
}
HTTP Request:
POST /terms/paynowcard HTTP/1.1
Host: xxxxxxxx
Content-Type: application/json
Cache-Control: no-cache
{
"userForm": {
"cardNumber": "4111111111111111",
"paymentAmount": 123.33
},
"account": {
"Creditor": {
"Name": "big time creditor"
},
"accountId": "32432432432423"
}
}
You should make sure the BrandTemplateInfo object has the same name properties as the posted json property :
public class BrandTemplateInfo
{
public userForm userForm { get; set; }
public account account { get; set; }
}
public class account
{
public Creditor Creditor { get; set; }
public string accountId { get; set; }
}
public class Creditor
{
public string Name { get; set; }
}
public class userForm
{
public string cardNumber { get; set; }
public float paymentAmount { get; set; }
}
So that model binding would work with [FromBody] attribute .

How to edit body of HTTP POST request in .net core?

I need to edit data(body) of HTTP POST request before storing it to DB.
I am beginner and trying to save data into database but before saving I need to fetch data using value of one variable in received request
Extra Information : This may help
This is http post body
{
"message": "K E ?",
"senderId": "c24617c6-4680-4a8b-a010-cdf969ddd3f8",
"dateTime": "2018-09-01T20:06:06",
"request": "9cb31157-86b4-4eeb-b770-fc3a86f5f906"
}
here request is object and this "9cb311......f5f906" is request id what I want to do is to fetch object of request using this request id so I can store the data into DB
Something Like this
[HttpPost]
public async Task<IActionResult> PostConversation([FromBody] Conversation conversation)
{
Request str = conversation.Request;
var request = (from r in _context.Requests
where r == str
select r);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Conversation.Add(conversation);
await _context.SaveChangesAsync();
return CreatedAtAction("GetConversation", new { id = conversation.ConversationUniqueId }, conversation);
}
DB Model
public class Conversation
{
public Request Request { get; set; }
}
public class Request
{
public string RequestId { get; set; }
}
Sorry if I am not clear
To save Request to Conversation by requestId, there is no need to do like this, you could define navigation property.
public class Conversation
{
public string RequestId { get; set; }
public virtual Request Request { get; set; }
}
public class Request
{
public string Id { get; set; }
}
Then, you could save your current request directly with RequestId without retriving Request object from database.
It seems that you want to bind multiple conversations to one request and save them in the database.
Edit your model like this:
public class Conversation
{
public int ConversationId { get; set; }
public string RequestId { get; set; }
[ForeignKey(nameof(RequestId))]
public Request Request { get; set; }
}
public class Request
{
public string RequestId { get; set; }
[InverseProperty(nameof(Conversation.Request))]
public IEnumerable<Conversation> Conversations{ get; set; }
}
That will make EF understand what you wanna to do.
And modify your code in your action like:
[HttpPost]
public async Task<IActionResult> PostConversation([FromBody] Conversation conversation)
{
string requestId = conversation.RequestId;
var request = (from r in _context.Requests
where r.RequestId == requestId
select r);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Reject if given requestid can not find a request.
if (request == null)
{
return NotFound();
}
_context.Conversation.Add(conversation);
await _context.SaveChangesAsync();
return CreatedAtAction("GetConversation", new { id = conversation.ConversationUniqueId }, conversation);
}
This will save the conversation in your database and also link it to that request.

Using ReadAsAsync<T>() to deserialize complex Json object

I want to use ReadAsAsync() in my mvc project with .net 4.0. The result comes as null.
If I enter the uri to address bar, the result in chrome as(tag names are changed):
<ns2:MyListResponse xmlns:ns2="blablabla">
<customerSessionId>xxcustomerSessionIdxx</customerSessionId>
<numberOfRecordsRequested>0</numberOfRecordsRequested>
<moreResultsAvailable>false</moreResultsAvailable>
<MyList size="1" activePropertyCount="1">
<MySummary order="0">
<id>1234</id>
<name>...</name>
.
.
</MySummary>
</MyList>
</ns2:MyListResponse>
If I use the statement in code :
using (var client = new HttpClient())
{
var response = client.GetAsync(apiUri).Result;
var message = response.Content.ReadAsStringAsync().Result;
var result1 = JsonConvert.DeserializeObject<MyListResponse>(message);
var result2 = response.Content.ReadAsAsync<MyListResponse>().Result;
}
the message comes in string format as "{\"MyListResponse\":{\"customerSessionId\"...}" which corresponds to a json object as:
{"MyListResponse":
{"customerSessionId":"xxcustomerSessionIdxx",
"numberOfRecordsRequested":0,
"moreResultsAvailable":false,
"MyList":
{"#size":"1",
"#activePropertyCount":"1",
"MySummary":
{"#order":"0",
"id":1234,
"name":"...",
.
.
}
}
}
}
and the properties of result1 and result2 came as null or default values. Class definitions are below. I want to read the content as an object but I couldn't. What do you advice to solve this? What am I doing wrong? Thanks in advance.
public class MySummary
{
public int #Order { get; set; }
public string Id { get; set; }
public string Name { get; set; }
.
.
}
public class MyList
{
public int #Size { get; set; }
public int #ActivePropertyCount { get; set; }
public MySummary MySummary{ get; set; }
}
public class MyListResponse
{
public string CustomerSessionId { get; set; }
public int NumberOfRecordsRequested { get; set; }
public bool MoreResultsAvailable { get; set; }
public MyList MyList { get; set; }
}
I defined a new class as:
public class ResponseWrapper
{
public MyListResponse MyListResponse { get; set; }
}
then I used this wrapper with,
var result1 = JsonConvert.DeserializeObject<ResponseWrapper>(message);
var result2 = response.Content.ReadAsAsync<ResponseWrapper>().Result;
then it worked. I need only MySummary object but I should write more classes to make it work.
After reading your solution I came up with one that doesn't need an extra class:
private static async Task<U> Execute<U>(HttpClient client, string path)
{
U output = default(U);
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var jsonAsString = await response.Content.ReadAsStringAsync();
output = JsonConvert.DeserializeObject<U>(jsonAsString);
}
else
{
throw new ApplicationException(string.Format("Response message is not OK. Issues in action: {0}", path));
}
return output;
}
For the sake of future readers, I think the correct approach is using ReadAsAsync overload that takes IEnumerable<MediaTypeFormatter> and provide a formatter with the same settings used on the server for serialization. That should fix it.
It is possible to use at client ReadAsAsync with MyListResponse directly (in consequence without ResponseWrapper). To do this, you can define "BodyStyle = WebMessageBodyStyle.Bare" in the operation contract of "apiuri" in stead of "BodyStyle = WebMessageBodyStyle.Wrapped" (server side, i.e. service contract).