Swagger API wont allow me to post multiple IFormFIles - c#

Controller:
[HttpPost("CreateEmail")]
[Consumes("multipart/form-data")]
public async Task<ApiResponse<Guid>> PostEmailToProcess([FromForm]EmailToProcess email)
{
try
{
var emailToAdd = new EmailToProcessDto(email);
emailToAdd.Created = DateTime.Now;
var emailId = await _emailRepository.PostEmailAsync(emailToAdd);
foreach (var file in email)
{
if (email.IFormFile != null)
{
await _contentUploadService.Upload(file, emailId.Data);
}
}
etc
EmailToProcess Object:
{
public class EmailToProcess
{
public string From { get; set; }
public string To { get; set; }
...
public List<IFormFile> IFormFile { get; set; }
}
}
As you can see in the image, swagger is only letting me submit 1 FormFile. I am new to swagger and creating APIs so Im sure Im missing something small.

Related

WEB API post from uri/ Query string in post

i have a model
public partial class TalentVendorShots
{
public int Id { get; set; }
public string Email { get; set; }
public string One { get; set; }
public string Two { get; set; }
public string Three { get; set; }
public string Four { get; set; }
public string Five { get; set; }
public string Six { get; set; }
public string Seven { get; set; }
public string Eight { get; set; }
public string Nine { get; set; }
public string Ten { get; set; }
}
and basic controllers
[Route("api/[controller]")]
[ApiController]
public class TalentVendorShotsController : ControllerBase
{
private readonly champagneDatabase _context;
public TalentVendorShotsController(champagneDatabase context)
{
_context = context;
}
// GET: api/TalentVendorShots
[HttpGet]
public async Task<ActionResult<IEnumerable<TalentVendorShots>>> GetTalentVendorShots()
{
return await _context.TalentVendorShots.ToListAsync();
}
// GET: api/TalentVendorShots/5
[HttpGet("{id}")]
public async Task<ActionResult<TalentVendorShots>> GetTalentVendorShots(int id)
{
var talentVendorShots = await _context.TalentVendorShots.FindAsync(id);
if (talentVendorShots == null)
{
return NotFound();
}
return talentVendorShots;
}
// PUT: api/TalentVendorShots/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTalentVendorShots(int id, TalentVendorShots talentVendorShots)
{
if (id != talentVendorShots.Id)
{
return BadRequest();
}
_context.Entry(talentVendorShots).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TalentVendorShotsExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/TalentVendorShots
[HttpPost]
public async Task<ActionResult<TalentVendorShots>> PostTalentVendorShots(TalentVendorShots talentVendorShots)
{
_context.TalentVendorShots.Add(talentVendorShots);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTalentVendorShots", new { id = talentVendorShots.Id }, talentVendorShots);
}
// DELETE: api/TalentVendorShots/5
[HttpDelete("{id}")]
public async Task<ActionResult<TalentVendorShots>> DeleteTalentVendorShots(int id)
{
var talentVendorShots = await _context.TalentVendorShots.FindAsync(id);
if (talentVendorShots == null)
{
return NotFound();
}
_context.TalentVendorShots.Remove(talentVendorShots);
await _context.SaveChangesAsync();
return talentVendorShots;
}
private bool TalentVendorShotsExists(int id)
{
return _context.TalentVendorShots.Any(e => e.Id == id);
}
}
}
all of this works fine. i get information from the database fine. now i want to make a post to the table via uri. no body.for example
/api/TalentVendorShots/id=1,email=testemail should create a new record with id of 1 and email of testemail. how can i accomplish this?
The basic rule is, You should use POST if the action is not idempotent. Though you can pass the query parameters and no body to POST. But It would not make sense in this scenario. Basically query parameters are used to get/filter information.
Similar way many Web API testing tools like ARC, Swagger, and PostMan (chrome extension does not allow, but standalone application allows) does not allow to send body with the GET request. Though you can send the body in GET requests.

My xamarin app freezes when i try JsonConvert.DeserializeObject <T>

When I get a json response from HttpClient () and try to deselize, my Xamarin application freezes (UI works, but the code after in class ExecuteGetRequest line 15 does not work). What can it be because of?
No errors.
I call the method of obtaining a list of anime user
ShikimoriMain shikimoriMain = new ShikimoriMain();
var UserInformation = await shikimoriMain.GetUserInformation(Convert.ToInt64(UserID));
var UserAnimeList = await shikimoriMain.GetUserAnimeList(Convert.ToInt64(UserID), 1, 5);
string animeName = UserAnimeList.Anime[0].Anime.Name;
ShikimoriMain.GetUserAnimeList
public async Task<ShikimoriUserAnimeList> GetUserAnimeList(long id, int page, int limit)
{
string[] args = new string[] { ShikimoriCategories.UserID + "/" + id + ShikimoriCategories.UserAnimeList + $"?limit={limit}&page={page}" };
return await ExecuteGetRequest<ShikimoriUserAnimeList>(args);
}
ExecuteGetRequest
public async Task<T> ExecuteGetRequest<T>(string[] args) where T : class
{
T returnedObject;
using (var client = new HttpClient())
{
// client.BaseAddress = new Uri($"{httpApiv1}/{args}");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, httpApiv1 + String.Join("/", args));
request.Headers.TryAddWithoutValidation("User-Agent", "Search Anime");
HttpResponseMessage responseMessage = await client.SendAsync(request);
string json = await responseMessage.Content.ReadAsStringAsync(); // successfully get json
returnedObject = JsonConvert.DeserializeObject<T>(json); // after that the code is not executed
return returnedObject;
}
}
ShikimoriUserAnimeList
public class ShikimoriUserAnimeList
{
[JsonProperty()]
public List<GetAnime> Anime { get; set; }
}
public class GetAnime
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("anime")]
public Anime Anime { get; set; }
}
public class Anime
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("russian")]
public string NameRU { get; set; }
[JsonProperty("image")]
public AnimeImage AnimeImage { get; set; }
[JsonProperty("kind")]
public string King { get; set; }
[JsonProperty("score")]
public string Score { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("episodes")]
public int Episodes { get; set; }
}
public class AnimeImage
{
[JsonProperty("original")]
public string Original { get; set; }
[JsonProperty("preview")]
public string Preview { get; set; }
[JsonProperty("x96")]
public string ImageX96 { get; set; }
[JsonProperty("x48")]
public string ImageX48 { get; set; }
}
For the sake of completion:
An error was being thrown but was not visible in the device log. Wrapping the JsonConvert.DeserializeObject<T>(json) in a try catch block helped finding the Exceptionbeing thrown.
try
{
returnedObject = JsonConvert.DeserializeObject<T>(json); // after that the code is not executed
return returnedObject;
}
catch (Exception ex)
{
... (debug and fix the error that occurred)
}
I had same problem, I've realized that using HttpClient async will cause deadlock in winforms or xamarin (however it works well with Asp) and I changed these lines
HttpResponseMessage responseMessage = await client.SendAsync(request);
string json = await responseMessage.Content.ReadAsStringAsync(); // successfully get json
Like these (Make them work synchronous):
HttpResponseMessage responseMessage = client.SendAsync(request).Result;
string json = responseMessage.Content.ReadAsStringAsync().Result; // successfully get json
And change your method as default synchronous
Take a look at Here

ASP.NET Core WebAPI : custom InputFormatter validate Model State

I have used custom InputFormatters for creating a subset of request from the generic request that request body receives in API request.
var reqModel = new XmlSerializer(CurrentType).Deserialize(xmlDoc.CreateReader());
SubSetRequest model = ConvertToSubSetRequestObject(reqModel as BigRequest);
return InputFormatterResult.Success(model);
Now in controller ModelState.IsValid is not pointing to SubSetRequest but to BigRequest, which I have received request body
public ActionResult<object> Calculate(SubSetRequest request)
{
if (!ModelState.IsValid){ }
// other codes..
}
Any idea how can we validate ModelState against SubSetRequest type.
Important Classes :
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(options =>
{
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
options.InputFormatters.Insert(0, new XMLDocumentInputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
}
BigRequest.cs
[System.SerializableAttribute()]
public class BigRequest
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string Company { get; set; }
public string Designation { get; set; }
public string CompanyAddress { get; set; }
}
SubSetRequest.cs
[System.SerializableAttribute()]
public class SubSetRequest
{
public string Name { get; set; }
[Required] //This should tiger **Validation** error
public string Email { get; set; }
public string Address { get; set; }
}
XMLDocumentInputFormatter.cs
internal class XMLDocumentInputFormatter : InputFormatter
{
private Type CurrentType { get; set; }
public XMLDocumentInputFormatter()
{
SupportedMediaTypes.Add("application/xml");
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
using (var streamReader = new StreamReader(context.HttpContext.Request.Body))
{
CurrentType = typeof(BigRequest);
var xmlDoc = await XDocument.LoadAsync(streamReader, LoadOptions.None, CancellationToken.None);
var reqModel = new XmlSerializer(CurrentType).Deserialize(xmlDoc.CreateReader());
var model = ConvertToSubSetRequestObject(reqModel as BigRequest);
return InputFormatterResult.Success(model);
}
}
public SubSetRequest ConvertToSubSetRequestObject(BigRequest request)
{
var retObject = new SubSetRequest
{
Name = request.Name,
Address = request.Address
};
return retObject;
}
}
ValueController.cs
[HttpPost]
[Route("/api/Value/Calculate")]
public virtual ActionResult<object> Calculate(SubSetRequest request)
{
TryValidateModel(request);
if (ModelState.IsValid) // is giving as TRUE, even if EMAIL is NULL
{
var context = new ValidationContext(request, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
// this is working properly
var isValid = Validator.TryValidateObject(request, context, results);
}
return new ActionResult<object>(request.ToString());
}

How I can pass data from Controller to Views on Blazor?

I try create a project using Blazor(server-side) and create the views as .razor, and I also add the Controller. How can I pass data from controller to views .razor ?
I think I can create a public variable in the controller, that will be interaction Controller and .razor. But I don't know for the Code
I want get var u to the view
//Controller
[HttpPost("[action]")]
public void Save(IList<IFormFile> UploadFiles)
{
var u = UploadFiles.FirstOrDefault().FileName;
long size = 0;
size = SaveUploadedFiles(UploadFiles, size);
}
//View
<EjsUploader ID="UploadFiles">
<UploaderAsyncSettings SaveUrl="api/Save" RemoveUrl="api/Remove"></UploaderAsyncSettings>
</EjsUploader>
Syncfusion support here.
You can pass the data from the controller either using “Headers” or “ReasonPhrase” to the view in the success event as mentioned in the below code example.
[SampleDataController.cs]
public void Save(IList<IFormFile> UploadFiles)
{
long size = 0;
try
{
foreach (var file in UploadFiles)
{
var filename = ContentDispositionHeaderValue
.Parse(file.ContentDisposition)
.FileName
.Trim('"');
filename = hostingEnv.ContentRootPath.Replace("WebApplication6.Server", "WebApplication6.Client") + $#"\{filename}";
size += (int)file.Length;
if (!System.IO.File.Exists(filename))
{
using (FileStream fs = System.IO.File.Create(filename))
{
file.CopyTo(fs);
fs.Flush();
}
}
}
Response.Headers.Add("custom-header", "Syncfusion");
Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Syncfusion Upload";
}
catch (Exception e)
{
Response.Clear();
Response.StatusCode = 204;
Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "File failed to upload";
Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = e.Message;
}
}
[index.razor]
#using Newtonsoft.Json;
#using Newtonsoft.Json.Converters;
<EjsUploader Id="UploadFiles" AutoUpload="true" Success="#OnSuccess">
<UploaderAsyncSettings SaveUrl="api/SampleData/Save" RemoveUrl="api/SampleData/Remove"></UploaderAsyncSettings>
</EjsUploader>
<p>Additional Header Text is: #HeaderData</p>
<p>Additional Response Data is: #ResponseData</p>
#code{
public string HeaderData;
public string ResponseData;
public void OnSuccess(object args)
{
SuccessEventArgs eventArgs = JsonConvert.DeserializeObject<SuccessEventArgs>(args.ToString());
HeaderData = eventArgs.Response.Headers;
ResponseData = eventArgs.Response.StatusText;
this.StateHasChanged();
}
//Success event args class.
public class SuccessEventArgs
{
public object E { get; set; }
public FileInfo File { get; set; }
public string StatusText { get; set; }
public string Name { get; set; }
public string Operation { get; set; }
public ResponseEventArgs Response { get; set; }
}
public class ResponseEventArgs
{
public string Headers { get; set; }
public object ReadyState { get; set; }
public object StatusCode { get; set; }
public string StatusText { get; set; }
public bool withCredentials { get; set; }
}
}
And, you need to enable the allow header option in the Startup.cs file as mentioned in the below code example.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.AddCors(options =>
{
options.AddPolicy("EnableCORS", builder =>
{
builder.AllowAnyOrigin().AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials().Build();
});
});
}
We have prepared the sample and attached below.
Sample Link:
https://www.syncfusion.com/downloads/support/directtrac/242743/ze/WebApplication6_additional_data-460323506
Also, currently we are working on to provide the specific type for success event arguments and this support will be included in our patch release scheduled on mid of August 2019. We appreciate your patience until then.
You can track the status of the issue from the below feedback link.
Link: https://www.syncfusion.com/feedback/7647/need-to-provide-specific-type-for-success-event-arguments-in-the-uploader

Receive file and other form data together in ASP.NET Core Web API (boundary based request parsing)

How would you form your parameters for the action method which is supposed to receive one file and one text value from the request?
I tried this
public string Post([FromBody]string name, [FromBody]IFormFile imageFile)
And tried hitting it from Postman but it gives me 500 Internal Server Error. I also can't debug it because it never hits the first statement inside the action method where I've put my breakpoint.
Any idea how can we parse boundary based requests and extract file(s) and other textual field(s)? I am new to ASP.NET Core.
I had the similar issue and I solved the problem by using [FromForm] attribute and FileUploadModelView in the function as follow:
[HttpPost]
[Route("upload")]
public async Task<IActionResult> Upload([FromForm] FileUploadViewModel model, [FromForm] string member_code)
{
var file = model.File;
// ...
}
This is a quick solution for anyone who is facing the same issue:
You will use ajax to send the following formData
let formData: FormData;
formData = new FormData();
formData.append('imageFile', imageFile);
formData.append('name', name);
Then you will receive it in your controller like this:
public string Post(IFormCollection data, IFormFile imageFile)
Then you will access the data as you do normally:
var name = data["name"];
In HomeController.cs
using Microsoft.AspNetCore.Hosting;
.......
private IHostingEnvironment _environment;
public HomeController(IHostingEnvironment environment)
{
_environment = environment;
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(IFormCollection formdata)
{
var files = HttpContext.Request.Form.Files;
foreach (var file in files)
{
var uploads = Path.Combine(_environment.WebRootPath, "Images");
if (file.Length > 0)
{
string FileName = Guid.NewGuid(); // Give file name
using (var fileStream = new FileStream(Path.Combine(uploads, FileName), FileMode.Create))
{
file.CopyToAsync(fileStream);
}
}
}
}
In view - Index.cshtml
<form method="post" enctype="multipart/form-data" >
.....
</form>
You can try this code.
Thanks!!
I'm using the following code to accomplish this in order to parse a response from Mailgun, which comprises both files and text values.
Please note that "dashifying" is just so property names like "MessageHeaders" get turned into "message-headers"; obviously you should use whatever logic makes sense for your use case.
Controller:
using System;
using Microsoft.AspNetCore.Mvc;
using NuGet.Protocol.Core.v3;
namespace Potato
{
[Route("api/[controller]")]
public class MailgunController : Controller
{
[HttpPost]
public IActionResult Post()
{
MailgunEmail email = new MailgunEmail(Request);
return Ok(email.ToJson());
}
}
}
Model:
using System;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace Potato
{
public class MailgunEmail
{
public IEnumerable<IFormFile> Attachments { get; set; }
public string Recipient { get; set; }
public string Sender { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string BodyPlain { get; set; }
public string StrippedText { get; set; }
public string StrippedSignature { get; set; }
public string BodyHtml { get; set; }
public string StrippedHtml { get; set; }
public int? AttachmentCount { get; set; }
public int Timestamp { get; set; }
public string Token { get; set; }
public string Signature { get; set; }
public string MessageHeaders { get; set; }
public string ContentIdMap { get; set; }
public MailgunEmail(HttpRequest request)
{
var form = request.Form;
Attachments = new List<IFormFile>(form.Files);
foreach (var prop in typeof(MailgunEmail).GetProperties()) {
string propName = Dashify(prop.Name);
var curVal = form[propName];
if (curVal.Count > 0) {
prop.SetValue(this, To(curVal[0], prop.PropertyType), null);
}
}
}
private object To(IConvertible obj, Type t)
{
Type u = Nullable.GetUnderlyingType(t);
if (u != null) {
return (obj == null) ? GetDefaultValue(t) : Convert.ChangeType(obj, u);
} else {
return Convert.ChangeType(obj, t);
}
}
private object GetDefaultValue(Type t)
{
if (t.GetTypeInfo().IsValueType) {
return Activator.CreateInstance(t);
}
return null;
}
private string Dashify(string source)
{
string result = "";
var chars = source.ToCharArray();
for (int i = 0; i < chars.Length; ++i) {
var c = chars[i];
if (i > 0 && char.IsUpper(c)) {
result += '-';
}
result += char.ToLower(c);
}
return result;
}
}
}
This page helped me a lot https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads
so now in my code I have the controller method as:
public async Task<IActionResult> UploadFiles(UploadedFile ups)
and a class for the model as
public class UploadedFile
{
public string UploadName { get; set; }
public List<IFormFile> Files { get; set; }
}
and the form like
<form method="post" enctype="multipart/form-data" asp-controller="Files" asp-action="UploadFiles">
I wanted to send a complex object rather than a text value. And can do it like below as well.
Sending form data:
let data = new FormData();
data.append("file", file, file.name);
data.append("someData", JSON.stringify(someData));
Using form data in the controller:
public async Task<IActionResult> OnPostUploadAsync( IFormCollection data )
{
var files = data.Files;
if (data.TryGetValue("someData", out var someData))
{
//use files & some data (json string)
}
}
You Can Get Multi Images By Sample Code Through MultiPart.
First Inject IHttpContextAccessor To ConfigureService Method In
Startup Class.Then Call It With Constructor Injection In
Controller:
private readonly IHttpContextAccessor _httpContextAccessor;
public FileController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Action Method Can Have Response Or Not.
public async Task<object> UploadImage()
{
var fileBytes = new List<byte[]>();
var files =
_httpContextAccessor.HttpContext.Request.Form.Files;
foreach (var file in files)
{
if (file.Length > 0)
{
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
fileBytes.Add(memoryStream.ToArray());
}
}
}
// TODO: Write Code For Save Or Send Result To Another Services For Save
}
With the usual controller (.NET 5\6) code will look like this
[ApiController]
[Route("[controller]")]
public class UploadController : Controller
{
[HttpPost]
[Route("CreateUser")]
public async Task<IActionResult> CreateUser([FromForm]ApplicationUserCreationDto userCreationDto)
{
return Json(new
{
user = userCreationDto.UserName,
fileName = userCreationDto.Photo.FileName
});
}
}
public class ApplicationUserCreationDto
{
[Required]
public string UserName { get; set; }
public IFormFile Photo { get; set; }
}
With Minimal API
it depends on the .NET version, as native support for IFormFile/IFormFileCollection is available only with .NET 7
binding from form values is not supported anyway
I propose to use another workaround
I guess you're transmitting the file and text in proper JSON format.
You should create a new class which includes file and text as properties of type string. Add the new class to your method arguments with attribute[FromBody] and you'll be able to parse the image string according to your needs.
Another way would be accessing the whole request content via
await Request.Content.ReadAsStringAsync();
You would then have to parse the entire content, though.

Categories

Resources