Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebAPI core IFormFile always showing null

I have one front-end in angular 4, and one back-end in ASP.NET Core WebAPI, the functionality of them is, send data and an curriculum vitae of one people to one database which is SQL Server, but... always the IFormFile of method who catch the data and send file to the database is null.

I've already tried all type of solution that I found on the internet but none of them worked for me.

as response of the Post method i receive this exception

An unhandled exception occurred while processing the request.  
  NullReferenceException: Object reference not set to an instance of an object.  
  WebApplication.Controllers.CandidateController+<Post>d__4.MoveNext() in CandidateController.cs, line 60

Down here I pasted parts of the code of the project who do this.

github link for complete code:
front-end code
back-end code

HTML

<form class="col-md-6 offset-md-3" method="POST" enctype="multipart/form-data" #form="ngForm" (ngSubmit)="onSubmit(form)">
  <div class="form-group row">
    <label for="name" class="col-sm-2 col-form-label">Name</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" placeholder="Nome completo" id="name"  name="name" ngModel />
    </div>
  </div>
  <div class="form-group row">
    <label for="email" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="email" placeholder="Email" name="email" ngModel />
      <small id="emailHelp" class="form-text text-muted">
        We'll never share your email with anyone else.
      </small>
    </div>
  </div>
  <div class="form-group row">
    <label for="country" class="col-sm-2 col-form-label">Country</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="country" placeholder="Country" name="country" ngModel />
    </div>
  </div>
  <div class="form-group row">
    <label for="state" class="col-sm-2 col-form-label">State</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="state" placeholder="Estado" name="state" ngModel />
    </div>
  </div>
  <div class="form-group row">
    <label for="city" class="col-sm-2 col-form-label">City</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="city" placeholder="Cidade" name="city" ngModel />
    </div>
  </div>
  <div class="form-group row">
    <label for="file" class="col-sm-2 col-form-label">Curriculum</label>
    <div class="col-sm-10">
        <input type="file" id="file" name="file" ngModel />
    </div>
  </div>
  <div class="container text-center">
    <button type="submit" class="btn btn-outline-dark">Submit</button>
  </div>
</form>

Angular 4

onSubmit(form: NgForm) {
  const { file } = form.value;
  delete form.value.file;

  var data = new FormData();
  data.append('Candidates', JSON.stringify(form.value));
  data.append('file', file);

  console.log(form.value);
  console.log(file);

  const headers = new Headers();
  headers.append('Access-Control-Allow-Origin', '*');

  const options = new RequestOptions({headers: headers});

  this.http.post("http://localhost:54392/api/candidates", data, options)
    .subscribe(
      data => {
        console.log("Foi");
      },
      error => {
        console.log("Não foi");
      });
}

C#

[HttpPost("candidates")]
public async Task<IActionResult> Post(IFormFile file)
{
    var json = HttpContext.Request.Form["Candidates"];      
    var jsonTextReader = new JsonTextReader(new StringReader(json));
    var candidate = new JsonSerializer().Deserialize<Candidate>(jsonTextReader);

    if (!ModelState.IsValid) 
        return BadRequest();

    using (var memoryStream = new MemoryStream())
    {
        await file.OpenReadStream().CopyToAsync(memoryStream);
        candidate.CurriculumVitae = memoryStream.ToArray();
    }
    await dataBase.AddAsync(candidate);
    dataBase.SaveChanges();
    return Ok(candidate);
}
like image 460
lambda Avatar asked Nov 07 '17 18:11

lambda


1 Answers

Upload images in Angular 4 without a plugin provides a walkthrough of the process you're attempting to achieve. It uses @ViewChild to grab a reference to the file input in the DOM and then uses that when building up the FormData. In your scenario, this involves a few changes, as follows:

  1. Replace ngModel on the file input in your HTML with #file. This creates a template reference variable that can be accessed inside your component when using @ViewChild in the next step.
  2. Add @ViewChild('file') fileInput; to the component, above your constructor. This links the #file template reference variable to your component code.
  3. Remove the following code from your component:

    const { file } = form.value;
    delete form.value.file;
    

    The file property will no longer exist at this point, so there's nothing to delete.

  4. Replace data.append('file', file); with the following:

    let fileBrowser = this.fileInput.nativeElement;
    if (fileBrowser.files && fileBrowser.files[0]) {
        data.append("file", fileBrowser.files[0]);
    }
    

    This last block of code grabs a handle on the file and appends it to your FormData.

You can name the variables and template reference variable whatever you want. The only thing that needs to be specific is the string value file used in data.append must match the variable name used for your C# IFormFile variable.

As an aside, you can also remove your custom Headers and RequestOptions as setting Access-Control-Allow-Origin as a request header is doing nothing here. This header should be set on the response, by the server.

like image 135
Kirk Larkin Avatar answered Sep 27 '22 20:09

Kirk Larkin