WEB Api upload file and model - c#

How can i upload file and model parameters in mvc WEB API 2.
I have following code, which works just fine, if i remove model from the action, but with the model, I am receiving following error.
"message": "The request entity's media type 'multipart/form-data' is
not supported for this resource.", "exception_message": "No
MediaTypeFormatter is available to read an object of type
'CreateTicketDTO' from content with media type
'multipart/form-data'.",
[HttpPost]
[Route("api/support/tickets/")]
public async Task<HttpResponseMessage> Insert(CreateTicketDTO dto)
{
if(dto == null)
return Request.CreateResponse(HttpStatusCode.BadRequest, "Please supply required parameters");
var provider = new MultipartMemoryStreamProvider();
var a = await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
//Do whatever you want with filename and its binaray data.
}
using (_ticketService)
{
var ticket = await _ticketService.CreateNewTicket(dto);
return Request.CreateResponse(HttpStatusCode.OK, ticket);
}
}
I am creating a post request in Postman.

Related

How to post a string array using Flurl?

In UI, the input type for SourceOfWealthOrIncome is a checkbox. In the front-end Razor page, I am using Flurl to issue a POST request.
var baseUrl = configuration.GetSection("xxx").GetValue<string>("Url");
string endPoint = "/Api/test/";
string url = string.Format("{0}{1}{2}", baseUrl, endPoint, ecddClientId);
var response = await url.SetQueryParams(new { id = ecddClientId, dealerCode })
.PostMultipartAsync(mp =>
{
mp = mp.AddString("SourceOfWealthOrIncome", postData.SourceOfWealthOrIncome);
foreach (var doc in postData.SupportingDocs)
{
mp = mp.AddFile("SupportingDocs", doc.FileContent, doc.FileName);
}
});
Troubleshoot: If I changed from SourceOfWealthOrIncome[] to SourceOfWealthOrIncome, I can see the values of the checkbox for example "value1, value2". But I can't change the endpoint code. This is the code for endpoint in C# in .NET Framework 4.x.
[HttpPost]
public async Task<IHttpActionResult> PutEnhancedCdd(int id, string dealerCode)
{
string root = Path.GetTempPath();
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
var sourceOfWealthOrIncome = provider.FormData["SourceOfWealthOrIncome[]"];
return Ok();
}
How do I change my front-end code using Flurl so that I can send a string object over to this current endpoint?

.net core 2.1 "POST" an IFormFile using Postman - the application completed without reading the entire request body

I'm working on a dotnet core WebAPI 2.1 and I can't find a way of sending to into the Body an image.
The controller looks like this:
[HttpPost("api/image")]
public IActionResult Post([FromBody]IFormFile file)
{
var filePath = Path.GetTempFileName();
if (file.Length > 0)
{
return Ok();
}
return BadRequest();
}
And this is my Postman call:
This call is never finishing as the Kestrel is failing
I've already tried to use Consumes
[Consumes("application/json", "application/json-patch+json", "multipart/from-data")]
Also in postman, I have set Content-Type to multipart/form-data
I found the solution for .Net Core 2.1/2.2 here
POST multiple files from POSTMAN
POST single file from POSTMAN
[HttpPost]
public async Task<IActionResult> UploadSingleFile([FromForm(Name = "file")] IFormFile file)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var IDsList = new IDsList();
try
{
var id = SaveFile(file);
IDsList.Files.Add(new IDsList.FileInfo() { id = id, fileName = file.FileName });
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Content(JsonConvert.SerializeObject(IDsList), "application/json");
}
[HttpPost("UploadFiles")]
public async Task<IActionResult> UploadFiles([FromForm(Name = "files")] ICollection<IFormFile> files)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
//var files = Request.Form.Files?.GetFiles("files");
String message = String.Empty;
int filesCounter = 0;
var IDsList = new IDsList();
foreach (var file in files)
{
if (file.Length == 0)
message = message + $"errorDescription {file.FileName}\n";
try
{
var id = SaveFile(file);
IDsList.Files.Add(new IDsList.FileInfo() { id = id, fileName = file.FileName });
filesCounter++;
}
catch(Exception ex)
{
message = $"{message}{ex.Message}\n";
}
}
IDsList.message = $"Amount: {filesCounter}.\n{message}";
return Content(JsonConvert.SerializeObject(IDsList), "application/json");
}
Try doing it like this. Use the same request in postman you are using now. This is just crude boilerplate method but you get the idea.
Also, dont forget to set headers of your request in postman to: Content-Type: multipart/form-data
[HttpPost]
[Route("api/image")]
public async Task<IHttpActionResult> InsertNewMerchant()
{
// your form data is here
var formData = HttpContext.Current.Request.Form;
HttpFileCollection files = HttpContext.Current.Request.Files;
if (files.Count == 1)
{
// this is the file you need
var image = files[0];
// do something with the file
}
return StatusCode(System.Net.HttpStatusCode.Created);
}
Seems like there is an error problem with Postman macOS application. When I'm using the postman chrome extension everything works as expected.
MacOS app
Chrome extension
I tried and it worked. Maybe you forget to do something.
Remove [FromBody] attribute.
[HttpPost("api/image")]
public IActionResult Post(IFormFile file)
{
var filePath = Path.GetTempFileName();
if (file.Length > 0)
{
return Ok();
}
return BadRequest();
}
Postman automatically attaches the correct Content-Type, select form-data option in body section and add your file with file key.
It should work.
I know this has been answered, but you seem to having decided for an workaround. There is nothing wrong with your endpoint nor with the Postman request, I had the same issue and installing the native Postman for my OS solved the problem https://www.getpostman.com/apps

The token response was successfully returned: unsupported_grant_type

I'm migrating from .NET Core 1.1 to 2.0, and now I have to update my Authentication too.
I'm using OAuth and OpenIddict to .NET Core 2.0
When I'm sending the request to my connect/token I'm getting this:
OpenIddict.Server.OpenIddictServerHandler[0] The token response was
successfully returned: {
"error": "unsupported_grant_type",
"error_description": "The specified 'grant_type' parameter is not
supported."
}.
This is my request method:
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, $"{url}/connect/token");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = clientId,
["client_secret"] = clientSecret,
["pessoaid"] = pessoaId,
["usuarioid"] = usuarioId,
["conta"] = conta,
["cpfcnpj"] = userDoubleCpf,
["fonteDados"] = fonteDados,
["userIdsLogged"] = userIdsLogged
});
var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
var result = JObject.Parse(await response.Content.ReadAsStringAsync());
if (result["error"] != null)
{
throw new InvalidOperationException("An error occurred while retrieving an access token.");
}
return result;
}
My OpenIddictApplications is generated when an application is linked to the user account, so the ClientId and Secret is generated, when a login request is send to my API and retrieve the respective values.
I have folowed the oppeniddict documentation and I have included everything in my Startup.cs
This is my AuthorizationController:
[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
Debug.Assert(request.IsTokenRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (request.IsClientCredentialsGrantType())
{
// Note: the client credentials are automatically validated by OpenIddict:
// if client_id or client_secret are invalid, this action won't be invoked.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted);
if (application == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "The client application was not found in the database."
});
}
// Create a new authentication ticket.
var ticket = CreateTicket(request, application);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
}
I'm generating the AuthenticationTicket and returning this.
Any idea about what might be causing this kind of badrequest when I try to send the request to take my token?
This happens because you do not configure the client credentials flow on you Startup.cs.
See the example: https://github.com/openiddict/openiddict-samples/blob/dev/samples/ClientCredentialsFlow/AuthorizationServer/Startup.cs
Attention for line 52:
// Enable the client credentials flow.
options.AllowClientCredentialsFlow();

Request.Content.IsMimeMultipartContent() fails

I'm trying to upload a file using the Html2 input type="file" and an angular2 http.post request. When the request reaches the web api, it fails in the
Request.Content.IsMimeMultipartContent()
It doesn't fail when submitting the request using Postman (when I don't include Content-Type in the header because postman takes care of it).
See my code:
Html:
<input type="file" (change)="fileChange($event)" placeholder="Upload file" accept=".pdf,.doc,.docx,.dwg,.jpeg,.jpg">
Service function:
uploadFile(event) {
let fileUploadUrl = this.webApiFileUploadURL;
let fileList: FileList = event.target.files;
if(fileList.length > 0) {
let file: File = fileList[0];
let formData:FormData = new FormData();
formData.append('uploadFile', file, file.name);
let headers = new Headers();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = new RequestOptions({ headers: headers });
this._http.post(`${this.webApiFileUploadURL}`, formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log('success'),
error => console.log(error)
)
}
And the WebApi post request (fails at
if (!Request.Content.IsMimeMultipartContent()) ):
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent()) // Fails here
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
After a thorough research - I've succeeded:
No need to set the content-type header property when posting.
I've removed it from my angular2 http.post request and the
Request.Content.IsMimeMultipartContent() in the web-api post method passed (same as in postman)
If anyone else runs into this "The request entity's media type 'multipart/form-data' is not supported for this resource."
You may need to add this in you webapiconfig
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
Original Credit
After so much reading, my guess is that you need to save your uploaded files asychronously (i just refer to it as JQUERY_D_UPLOAD).
I've created this small ASP.NET C# async task below to get you started.
NOTE: should return a string eg return "This was created by onyangofred#gmail.com";
For more, find me in my turing.com/github/facebook/gmail accounts: (onyangofred)
public async Task<string> SaveFile()
{
for (int i = 0; i < Request.Files.Count; i++)
{
HttpPostedFileBase file = Request.Files[i];
using (var stream = new FileStream(Path.Combine("~/uploads", Guid.NewGuid().ToString() + Path.GetExtension(file.FileName)), FileMode.Create, FileAccess.Write, FileShare.Write, 4096, useAsync: true))
{
await file.InputStream.CopyToAsync(stream);
}
}
}

How to send files along with the viewModel using Web API or how to save using temporary data

I've read many stackoverflow posts with the similar problems as well as several blogs but I am still uncertain as how to solve my problem :(
I have angularJS directive that allows to upload files to the server. The code is like this:
[HttpPost]
[Route("UploadFile")]
public async Task<HttpResponseMessage> UploadFile()
{
// Check if the request contains multipart/form-data.
if (Request.Content.IsMimeMultipartContent("form-data"))
{
try
{
var resultOut = new List<FileUploadResult>();
var streamProvider = new MultipartMemoryStreamProvider();
streamProvider = await Request.Content.ReadAsMultipartAsync(streamProvider);
foreach (
var item in
streamProvider.Contents.Where(c => !string.IsNullOrEmpty(c.Headers.ContentDisposition.FileName))
)
{
FileUploadResult file = new FileUploadResult()
{
FileName = item.Headers.ContentDisposition.FileName,
// Content = fileBytes, // No need to pass the info back as we're not going to read it save it yet
Key = Guid.NewGuid().ToString(),
Type = item.Headers.ContentDisposition.DispositionType
};
resultOut.Add(file);
//using (Stream stFileSource = new MemoryStream(await item.ReadAsByteArrayAsync())) {
// byte[] fileBytes;
// fileBytes = new Byte[stFileSource.Length];
// stFileSource.Read(fileBytes, 0, Convert.ToInt32(stFileSource.Length));
// FileUploadResult file = new FileUploadResult()
// {
// FileName = item.Headers.ContentDisposition.FileName,
// // Content = fileBytes, // No need to pass the info back as we're not going to read it save it yet
// Key = Guid.NewGuid().ToString(),
// Type = item.Headers.ContentDisposition.DispositionType
// };
// resultOut.Add(file);
//}
}
return Request.CreateResponse(HttpStatusCode.OK, resultOut.ToArray());
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
Also directive saves the Files array into a property. My user form allows to remove some files / add more files and then I want to save the information from the form (somewhat complex view model) along with the files. I was unable to figure that problem so far. One possibility I see here is to save the files in the UploadFile method using Repository into a database. However, I would prefer to save that into some temporary table instead (e.g. #FileInfo table) and not the actual table. Or perhaps there is a way to save files (with its binary content) into some memory object so I will be able to get that content back when I am ready to save my model's data? Can you either show implementation of the temporary repository storage or give some other ideas for my dilemma?
Firstly, Your directive need to create a post request with 'multipart/form-data'.
Check this link for reference.
However, we use angular file upload to do this.
angular
.module('app', ['angularFileUpload'])
.controller('AppController', function($scope, FileUploader) {
$scope.uploader = new FileUploader(
{
url: 'Your/upload/url',
headers: {
'autorization': 'Bearer token if you need it'
},
onProgressItem: function () {
...
},
onSuccessItem: function (opt, data) {
...
},
onErrorItem: function (opt) {
...
}
});
//you may want to wrap the following in an event
var uploadItem = $scope.uploader.queue[uploader.queue.length - 1];
uploadItem.formData.push({
someData: "someData",
moreData: "moreData"
});
uploadItem.upload();
uploadItem.formData = [];
});
Then in your controller, you can do the following to retrieve what you need:
//your request
var request = HttpContext.Current.Request;
//your fields
var someData = request.Form["someData"];
var moreData = request.Form["moreData"];
//your file
var file = request.Files["file"];
Looks like a job for TempData:
TempData in ASP.NET MVC is basically a dictionary object derived from
TempDataDictionary. TempData stays for a subsequent HTTP Request as
opposed to other options (ViewBag and ViewData) those stay only for
current request. So, TempdData can be used to maintain data between
controller actions as well as redirects.
example:
//Controller Action 1 (TemporaryEmployee)
public ActionResult TemporaryEmployee()
{
Employee employee = new Employee
{
EmpID = "121",
EmpFirstName = "Imran",
EmpLastName = "Ghani"
};
TempData["Employee"] = employee;
return RedirectToAction("PermanentEmployee");
}
//Controller Action 2(PermanentEmployee)
public ActionResult PermanentEmployee()
{
Employee employee = TempData["Employee"] as Employee;
return View(employee);
}

Categories

Resources