2
votes

Passer l'erreur de validation Multer au composant React

J'apprends Multer avec Redux et React .

Mon routeur express est comme

render() {
        console.log(this.props.uploadImage);
}


const mapStateToProps = state => ( {
    uploadImage: state.addressReducer.uploadImage
} );


export default connect(mapStateToProps)(ModalElement);

Mon code Multer est comme ci-dessous

const addressReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'getAddresses': {
            return {
                ...state,
                controlModal: action.payload.valueModal,
                address: action.payload.addressData
            };
        }
        case 'uploadImage': {
            return {
                ...state,
                uploadImage: action.payload 
            };
        }
        default:
            return state;
    }
};

Mon action est comme ci-dessous

export const uploadImage = (formData, id, config) => dispatch => {
  return Axios.post('/api/address/upload', formData, config)
    .then(response => {
      dispatch({
        type: 'uploadImage',
        payload: response.data
      });
    })
    .catch(error => {
      dispatch({
        type: 'uploadImage',
        payload: error // I would like to pass error through here.
      });
      return false;
    });
};

Mon réducteur est comme ci-dessous

const uploadImage = (req, res, next) => {

    const storage = multer.diskStorage({
        destination: function(req, file, cb) {
            cb(null, './uploads/');
        },
        filename: function(req, file, cb) {
            cb(null, Date.now() + '-' + file.originalname);
        }
    });

    const fileFilter = (req, file, cb) => {
        if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
            cb(null, true);
        } else {
            cb(new Error('Try to upload .jpeg or .png file.'), false);
        }
    };

    const upload = multer({
        storage: storage,
        limits: {
            fileSize: 1024 * 1024 * 5
        },
        fileFilter: fileFilter
    }).single('addressImage');

    upload(req, res, function(error) {
        if (error) {
            // An error occurred when uploading
            res.status(500).json({
                message: error // I would like to send error from Here.
            });
            console.log(error);
        } else {
            if (req.file.filename === res.req.res.req.file.filename) {
                res.status(200).json({
                    message: 'File uploaded',
                    file: req.file.filename
                });
            }
            return;
        }
    });
}

Je voudrais j'aime obtenir une erreur dans mon composant est comme ci-dessous

router.post('/upload', addressController.uploadImage);

Ma sortie de console est comme ci-dessous

 entrez la description de l'image ici

Comment puis-je obtenir une erreur Essayer de télécharger un fichier .jpeg ou .png. dans mon composant React pendant que j'essaie de télécharger un fichier sans l'extension .jpeg et .png?


7 commentaires

son erreur de serveur interne 500 alors, pouvez-vous s'il vous plaît poster le journal des erreurs que vous obtenez côté serveur?


Merci @VikashSingh. Si j'utilise ce code upload (req, res, function (error) {if (error) {console.log (error); // Je dois transmettre cette erreur à React} else {if (req.file. filename === res.req.res.req.file.filename) {res.status (200) .json ({message: 'File uploadé', file: req.file.filename});} return;}}) ; . Je reçois cette sortie i.stack.imgur.com/mJitG.png


Il existe un très beau tutoriel qui résoudra vos doutes concernant multer, veuillez le parcourir une fois: scotch.io/tutorials/express-file-uploads-with-multer J'espère que cela vous aidera. Si vous rencontrez plus de défis, faites-le moi savoir.


selon la capture d'écran, il échoue à la validation du fichier. téléchargez-vous un fichier image .jpeg ou .png?


Merci @VikashSingh. Je voudrais obtenir ce message de validation de fichier dans le composant React.


Merci @VikashSingh. J'ai vu votre tutoriel précédemment mais cela ne se concentre pas sur mon problème.


continuons cette discussion dans le chat .


3 Réponses :


3
votes

vous n'êtes pas obligé d'envoyer 500 codes d'état à la place, vous devez envoyer 400

 res.status(400).json({
            message: error // I would like to send error from Here.
        });


5 commentaires

Merci @Vikash Singh. J'ai utilisé ce code res.status (200) .json ({error}); et j'ai obtenu cette erreur i.stack.imgur.com/s9Ax8.png.


Il indique qu'une erreur de stockage peut être due à une manière incorrecte d'accorder le chemin du répertoire ou les autorisations de répertoire. Essayez de donner le chemin absolu complet et d'attribuer les autorisations appropriées.


Merci @Vikash Singh. Le chemin du répertoire est correct. Si j'utilise .png ou .jpeg, tout fonctionne correctement. Je souhaite transmettre le message de validation à React. Si j'utilise if (error) {console.log (error); } le message est imprimé dans le Terminal. Merci.


res.status (400) .json ({erreur}); cela devrait résoudre


Bonjour, Pouvez-vous développer l'objet request dans votre capture d'écran précédente et me montrer ce qu'il contient?



3
votes

L ' Erreur ne se résout pas en un json valide lorsqu'elle est transmise via res.json () et donc, elle est supprimée.

pour accéder au message "Essayez de télécharger un fichier .jpeg ou .png." , vous devez mettre à jour le code Multer comme ceci:

.catch(error => {
    dispatch({
        type: "uploadImage",
        /** error.data is the response. We want the `message` property from it */
        payload: error.data.message // I would like to pass error through here.
    });
    return false;
});


0 commentaires

2
votes

Voici comment j'ai pu y parvenir pour un microservice d'avatar que j'ai créé pour fonctionner avec mon application principale.

AVERTISSEMENT : Cette explication parcourt tout le flux, elle peut donc être longue et redondante si vous la comprenez déjà.


Créez une configuration axios. h2>

Tout d'abord, vous devez créer une configuration axios . Par défaut, axios n'affichera pas le err renvoyé par le serveur, à la place, il affichera simplement un objet Error générique. Vous devrez mettre en place un intercepteur

utils/axiosConfig.js[

class RenderMessages extends Component {
  shouldComponentUpdate = nextProps =>
    this.props.serverError !== '' ||
    nextProps.serverError !== '' ||
    this.props.serverMessage !== '' ||
    nextProps.serverMessage !== '';

  componentDidUpdate = () => {
    const { serverError, serverMessage } = this.props;
    if (serverError || serverMessage) {
      const notification = serverError
        ? serverErrorMessage(serverError)
        : serverSuccessMessage(serverMessage);
      this.renderNotification(...notification);
    }
  };

  renderNotification = ({ noteType, description }) => {
    notification[noteType]({
      message: noteType === 'error' ? 'Error' : 'Update',
      description,
      icon: descriptionLayout(noteType),
    });
    setTimeout(() => this.props.resetServerMessages(), 3000);
  };

  render = () => null;
}

export default connect(
  state => ({
    serverError: state.server.error, // retrieving the error from redux state
    serverMessage: state.server.message,
  }),
  { resetServerMessages },
)(RenderMessages);

Le flux du client au serveur vers le client.

Client

Un utilisateur soumet un formulaire avec formData et cela déclenche un créateur action :

uploadAvatar thunk action creator (qui est une promesse en attente d'une réponse ou erreur de notre serveur) :

import * as types from 'types';

const serverInitialState = {
  error: '',
  message: '',
};

const ServerReducer = (state = serverInitialState, { payload, type }) => {
  switch (type) {
    case types.RESET_SERVER_MESSAGES:
      return { ...state, error: '' }; 
    case types.SERVER_ERROR:
      return { ...state, error: payload }; // the server err is stored to redux state as "state.server.error"
    case types.SERVER_MESSAGE:
      return { ...state, message: payload };
    default:
      return state;
  }
};

export default ServerReducer;

Serveur

La requête POST est sélectionnée par notre route express :

.catch(err => // our server "err" is passed to here from the interceptor
   dispatch({ type: types.SERVER_ERROR, payload: err.toString() }), // then that "err" is passed to a reducer
); 

La requête atteint cette route: '/ api / avatar / create' , passe par une fonction middleware (voir ci-dessous) avant de passer par une autre fonction middleware saveImage , avant de finalement passer par un contrôleur create .

Client

Le serveur renvoie une réponse au client. La réponse de notre serveur passe par le axios configuration interceptor , qui détermine comment gérer la réponse ou l ' erreur qui a été renvoyé de notre serveur. Il transmettra ensuite la réponse ou error au .then () ou .catch () du Créateur de action . Le créateur de l ' action la transmet au réducteur , qui met à jour l'état de redux , qui met ensuite à jour le composant connect ed .


Configuration du serveur (microservice).

Partout où vous définissez vos middlewares express (ex: bodyParser , cors ou passeport etc.), vous voudrez créer une fonction middleware multer (chaque fois qu'un fichier est téléchargé, il passe par cette fonction premier):

avatarAPI.interceptors.response.use(
  response => response,
  error => {
    const err = get(error, ['response', 'data', 'err']); // this checks if "error.response.data.err" is present; which it is, and is now "That file extension is not accepted!"

    return err ? Promise.reject(err) : Promise.reject(error.message); // that err string gets returned to our uploadAvatar action creator's "catch" block
   },
);

services /saveImage.js (après avoir traversé la fonction middleware ci-dessus, le résultat est passé à cette fonction middleware saveImage )

const fs = require("fs");
const sharp = require("sharp");
const { createRandomString } = require('../../utils/helpers');

module.exports = (req, res, next) => {   
  // if the file failed to pass the middleware function above, we'll return the "req.err" as "err" or return a string if "req.file" is undefined. In short, this returns an "error.response.data.err" to the client.
  if (req.err || !req.file) {
    return res.status(400).json({ err: req.err || "Unable to process file." });
  }

  const randomString = createRandomString();

  const filename = `${Date.now()}-${randomString}-${req.file.originalname}`;
  const filepath = `uploads/${filename}`;

  const setFile = () => {
    req.file.path = filepath;
    return next();
  };

  /\.(gif|bmp)$/i.test(req.file.originalname)
    ? fs.writeFile(filepath, req.file.buffer, (err) => {
      if (err) return res.status(400).json({ "Unable to process file." });
      setFile();
    })
    : sharp(req.file.buffer)
      .resize(256, 256)
      .max()
      .withoutEnlargement()
      .toFile(filepath)
      .then(() => setFile());
};

Si ce qui précède passe, il passe ensuite req (qui contient req.file et toutes ses propriétés) au contrôleur create , qui dans mon cas, stocke un chemin vers le fichier (/uploads/name-of-file.ext), et un str pour récupérer l'image ( http: // localhost: 4000 / uploads / name-of- file.ext ) dans ma base de données. Dans mon cas, cette chaîne est ensuite renvoyée au client pour être stockée dans l'état redux, puis mise à jour en tant qu'avatar de l'utilisateur (lors du passage d'une chaîne dans un avatarurl.png , il envoie une requête GET au microservice).


La validation échoue.

Disons à un utilisateur a essayé de télécharger une image .tiff . Il passe par notre fonction middleware express multer, qui déclenche l'erreur "Cette extension de fichier n'est pas acceptée!" , cette erreur est renvoyée via req.err code> à saveImage , qui renvoie le req.err comme: return res.status (400) .json ({err: req.err});

Du côté client, cet err traverse notre axios interceptor:

app.use(cors({ origin: "http://localhost:3000" }));
app.use(bodyParser.json());
app.use(
    multer({
      limits: {
        fileSize: 10240000,
        files: 1,
        fields: 1
      },
      fileFilter: (req, file, next) => {
        if (!/\.(jpe?g|png|gif|bmp)$/i.test(file.originalname)) {
          req.err = "That file extension is not accepted!"; // this part is important, I'm attaching the err to req (which gets passed to the next middleware function => saveImage)
          next(null, false);
        }
        next(null, true);
      }
    }).single("file")
  );
...etc

Le bloc catch du créateur d'action uploadAvatar est déclenché:

app.post('/api/avatar/create', saveImage, create);

Le réducteur prend le serveur err et le stocke dans l'état:

import { avatarAPI } from '../utils/axiosConfig'; // import the custom axios configuration that was created above
import * as types from 'types';

const uploadAvatar = formData => dispatch =>
  avatarAPI
    .post(`avatar/create`, formData) // this makes a POST request to our server -- this also uses the baseURL from the custom axios configuration, which is the same as "http://localhost:4000/api/avatar/create"
    .then(({ data }) => {
      dispatch({ type: types.SET_CURRENT_AVATAR, payload: data.avatarurl });
    })
    .catch(err => // this will return our server "err" string if present, otherwise it'll return a generic Error object. IMPORTANT: Just in case we get a generic Error object, we'll want to convert it to a string (otherwise, if it passes the generic Error object to our reducer, stores it to redux state, passes it to our connected component, which then tries to display it... it'll cause our app to crash, as React can't display objects)
      dispatch({ type: types.SERVER_ERROR, payload: err.toString() }),
    );

Un composant connect ed récupère cet état .server.error et l'affiche (ne vous inquiétez pas trop de la logique ici, juste que c'est un composant connecté affichant le state.server.error comme serverError code>):

import get from 'lodash/get';
import axios from 'axios';

export const avatarAPI = axios.create({
  baseURL: 'http://localhost:4000/api/', // this makes it easier so that any request will be prepended with this baseURL
});

avatarAPI.interceptors.response.use(
  response => response, // returns the server response
  error => {
    const err = get(error, ['response', 'data', 'err']); // this checks if "error.response.data.err" is present (this is the error returned from the server); VERY IMPORTANT: this "err" property is specified in our express middlewares/controllers, so please pay attention to the naming convention.

    return err ? Promise.reject(err) : Promise.reject(error.message); // if the above is present, return the server error, else return a generic Error object
  },
);

Le résultat final est Cette extension de fichier n'est pas acceptée! co de> err est affiché à l'utilisateur:

 entrez la description de l'image ici


0 commentaires