Vue.js axios post - upload images. Getting status 200 with 400 error

Hi,
Thank you in advance for your time. I have to spend hours trying to find a way, how to fix it.
I want to upload images to Azure Storage. On Backend written in C# is an ImagesController which help to upload those images.
Below is a code snippet which is resposible for uploading images:

 let url = this.configuration.databaseURL + "/api/Images/";
        const siteID = this.clinic.AdditionalInformation.siteId;
        const body = {
          Id: siteID,
          Image: this.logo.imagedata.split(",")[1],
          Type: "Logo",
          ContentType: "image/png" // temporary hardcoded, this is type of file, do I really need this if in header is the "Content-Type"?
        };

        const config = {
          headers: { "Content-type": "multipart/form-data" }
        };

        await this.axios
          .post(url, body, config)
          .then(response => {
            console.log("post response", response);
            this.inputobject.LogoImageId = response.data.ID;
          })
          .catch(error => console.log("error.response", error.response));

The output JSON in Postman which is working is:

{
    "Id": "321321",
    "Image": "/9j/4AAQSkZJRgABAQAAAQABAAD/...BDkl34//9k=",
    "Type": "Logo",
    "ContentType": "image/png"
}

When POST method is running I get status 200, but there is an error inside response.

This is the whole vue.js component:

<template>
  <div>
    <div
      class="eig-clinic-portal__img eig-clinic-portal__img--added"
      :style="{ backgroundImage: logoUrl }"
    >
      <div class="eig-clinic-portal__img-edit">
        <div class="eig-clinic-portal__input-file-wrapper">
          <button class="eig-btn eig-btn-primary eig-btn-xs">
            <label for="cliniclogoedit">{{ $t("newlogo") }}</label>
          </button>
          <input
            type="file"
            id="cliniclogoedit"
            name="cliniclogo"
            accept="image/png, image/jpeg, image/gif, image/svg+xml"
            ref="logo"
            @change="handleLogoUpload()"
          />
        </div>
      </div>
    </div>

    <div class="eig-clinic-portal-wrapper__item eig-clinic-portal__center">
      <div v-if="saveButtonClicked && !hasValidationErrors">
        <div v-if="successClass">
          <div v-if="!state.addedReferralService">
            <div class="eig-clinic-portal__loading">
              <i class="fas fa-sync fa-spin"></i>
            </div>
          </div>
        </div>
      </div>
      <input
        type="button"
        :value="$t('save')"
        class="eig-btn eig-btn-primary"
        v-bind:class="
          this.state.saving ? 'eig-btn-primary--disabled' : 'eig-btn-primary'
        "
        :disabled="this.state.saving"
        @click="updateClinic"
      />
    </div>

    <div v-if="saveButtonClicked && !hasValidationErrors">
      <div v-if="successClass">
        <SaveSuccess v-if="!state.addedReferralService">
          {{ $t("sucess") }}
        </SaveSuccess>
        <SaveInApproval v-else>
          {{ $t("publishedAfterApproval") }}
        </SaveInApproval>
      </div>
      <Error v-if="errorClass">
        {{ $t("updatefailed") }}
      </Error>
    </div>
  </div>
</template>

<script>
var _ = require("lodash");
import i18n from "../i18n";

export default {
  name: "editClinic",
  data() {
    return {
      languageSuffix: "",
      clinic: {},
      configuration: {},
      successClass: false,
      errorClass: false,
      saveButtonClicked: false,
      valueChanged: false,
      inputobject: {},
      logo: {
        imagedata: "",
        added: false,
        changedId: 0
      },
      state: {
        applicationErrors: [],
        loading: false,
        validationErrors: [],
        saving: false
      }
    };
  },
  created: async function() {
    this.configuration = this.$parent.configuration;
    i18n.locale = this.configuration.userLanguage;

    if (this.configuration.userLanguage) {
      this.languageSuffix = "/" + this.configuration.userLanguage;
    }

    var onlyNumeric = RegExp("^\\d+$");
    if (onlyNumeric.test(this.$route.params.id)) {
      this.state.loading = true;
      let uri =
        this.configuration.databaseURL +
        "/api/clinics/" +
        this.$route.params.id +
        "/ClinicShow" +
        this.languageSuffix;
      await this.axios
        .get(uri)
        .then(response => {
          this.clinic = response.data;

          if (this.clinic.LogoImage && this.clinic.LogoImage.ID) {
            this.inputobject.LogoImageId = this.clinic.LogoImage.ID;
          }
        })
        .catch(error => {
          this.state.applicationErrors.push(error);
        })
        .finally(() => {
          this.state.loading = false;
        });
    }
  },

  computed: {
    logoUrl: function() {
      if (this.logo.imagedata !== "") {
        return "url(" + this.logo.imagedata + ")";
      } else if (this.clinic.LogoImage) {
        return (
          "url(data:image/jpeg;base64," + this.clinic.LogoImage.Picture + ")"
        );
      }
    },
    hasValidationErrors: function() {
      return _.keys(this.state.validationErrors).length > 0;
    }
  },
  methods: {
    loadImage: function(file, callback) {
      var _URL = window.URL || window.webkitURL;
      var img = new Image();
      var self = this;
      img.onload = function() {
        if (this.width < 770) {
          alert(self.$i18n.t("images.imagetoosmall"));
          callback(false);
        } else {
          callback(true);
        }
      };
      img.src = _URL.createObjectURL(file);
    },
    validatePicture: function(file, callback) {
      var self = this;
      if (file.type.indexOf("image") == -1) {
        alert(self.$i18n.t("images.wrongfiletype"));
        callback(false);
        return;
      }
      if (
        !(
          file.type.indexOf("png") > -1 ||
          file.type.indexOf("jpeg") > -1 ||
          file.type.indexOf("gif") > -1 ||
          file.type.indexOf("svg") > -1
        )
      ) {
        alert(self.$i18n.t("images.wrongfiletype"));
        callback(false);
        return;
      }
      if (file.size > 2500000) {
        alert(self.$i18n.t("images.imagetoobig"));
        callback(false);
        return;
      }
      if (file.size > 1000000) {
        alert(self.$i18n.t("images.recommendedsize"));
      }
      this.loadImage(file, function(result) {
        callback(result);
      });
    },
    handleLogoUpload: function() {
      var file = this.$refs.logo.files[0];
      var self = this;
      this.validatePicture(file, function(result) {
        if (result) {
          var reader = new FileReader();

          reader.onload = e => {
            self.logo.imagedata = e.target.result;
          };

          reader.readAsDataURL(file);

          if (self.clinic.LogoImage && self.clinic.LogoImage.ID) {
            self.logo.changedId = self.clinic.LogoImage.ID;
            self.logo.added = false;
          } else {
            self.logo.added = true;
            self.logo.changedId = 0;
          }
        }
      });
    },
    setValueChanged: function() {
      this.valueChanged = true;
    },

    async updateClinic() {
      this.state.applicationErrors = [];
      this.errorClass = false;
      this.state.saving = true;
      this.saveButtonClicked = true;
      this.state.addedReferralService = false;

      if (this.logo.added || this.logo.changedId !== 0) {
        let url = this.configuration.databaseURL + "/api/Images/";
        const siteID = this.clinic.AdditionalInformation.siteId;
        const body = {
          Id: siteID,
          Image: this.logo.imagedata.split(",")[1],
          Type: "Logo",
          ContentType: "image/png"
        };

        const config = {
          headers: { "Content-type": "multipart/form-data" }
        };

        await this.axios
          .post(url, body, config)
          .then(response => {
            console.log("post response", response);
            this.inputobject.LogoImageId = response.data.ID;
          })
          .catch(error => console.log("error.response", error.response));
      }
    },

    async loadClinic() {
      let uri =
        this.configuration.databaseURL +
        "/api/clinics/" +
        this.$route.params.id +
        "/ClinicShow" +
        this.languageSuffix;
      await this.axios
        .get(uri)
        .then(response => {
          this.clinic = response.data;

          if (this.clinic.LogoImage && this.clinic.LogoImage.ID) {
            this.inputobject.LogoImageId = this.clinic.LogoImage.ID;
          }
        })
        .catch(error => {
          console.log(error);
        });
    }
  }
};
</script>


Hi!

Did you know that VueJs has dedicated forum just like this one :)? It is likely you will find your answer there.

I see you are using Azure. Do you have access to back-end code? If the controller method returns a OK response code with response message containing the error code then this might be your case.

As far as I remember, file upload- bad request means bad file to be uploaded (back-end doesn’t understand it).

You obviously are trying to use base64 but that is not a optimal way to upload files as they can grow really big and string will not hold really big values. E.g. files > 20 MB will start to show issues.

In fact I am right now working on React version of your case.

Back-end - controller method for single file upload using form data
c#, .NET Core 3.1

[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
    try
    {
        // here I upload to blob storage and retrieve file path for later processing
        var filePathResult = await _fileUploadHelper.UploadFileAsync(file);

        if (string.IsNullOrEmpty(filePathResult))
        {
            return StatusCode(500, "Failed to upload file.");
        }
        else
        {
            return Ok(/* some OK response for  front-end handling */);
        }
    }
    catch (Exception ex)
    {
        // log error via some logger you have
        return StatusCode(500); // handle error cases in front-end
    }
}

Front-end - single File upload function using files from FileList retrieved from <input type='file' />:
Typescript 3.5.1

export const uploadFile = async (
  file: File
): Promise<FileUploadResponse | undefined> => {
  try {
    // the simplest way to use multiform data
    const formData = new FormData();
    // key name = back-end parameter name
    formData.append('file', file);

    const response = await fetch('/UploadFile', {
      method: 'POST',
      body: formData,
      // do not add any extra headers - those will be auto added by browser (Postman too)
    });
    // handle your defined back-end error codes
    if (response.status === 500) {
      console.error(response.statusText, file.name, file.size);
      return undefined;
    }
    // await good response
    return await response.json();
  } catch (e) {
    console.error(e);
    // return something for handling this function from outside
    return undefined;
  }
};

Hope this helps!