问题
I am mostly finished with a Udemy tutorial by Brad Traversy called "MERN Stack Front To Back," and am running into a couple similar errors trying to expand upon his app by giving users the option to upload a photo using Cloudinary. Both errors are related to CORS requests.
Before getting into it, it might be important to know about this app is that it uses npm Concurrently to run the Node server then the React/Redux client with the same npm run dev command -- and uses no middleware to deal with CORS requests. So my first question is, How does this setup get around the need for the middleware? It seems to me like they're still running separate servers...
Regardless of why Brad Traversy's app gets away with no such middleware, when I added a new action of my own to the app's landing page that made a request to the api in the same way the other components do, eg:
componentDidMount() {
this.props.getAllAlerts();
}
and
export function getAllAlerts() {
const request = axios.get(`${ROOT_URL}/api/alert`);
return {
type: GET_ALL_ALERTS,
payload: request
};
}
I received the following error: "Failed to load http://localhost:5000/api/alert: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access."
I actually solved this error entirely simply by adding the npm cors middleware and using it in my api server.
app.use(cors());
Still, I would like to know why it happened in the first place when the other components do axios requests to the api with no need for it -- because maybe it will help understand why ran into a very similar error later on when adding a component that uploads a photo from the browser directly to Cloudinary. Here's the action:
export const uploadFile = event => {
const file = event.target.files[0];
const CLOUDINARY_URL = `https://api.cloudinary.com/v1_1/${myCloudinaryApi}/upload`;
const CLOUDINARY_UPLOAD_PRESET = CLOUDINARY_UPLOAD_PRESET;
const formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", CLOUDINARY_UPLOAD_PRESET);
return dispatch => {
return axios({
url: CLOUDINARY_URL,
method: "POST",
skipAuthorization: true,
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: formData
})
.then(response => {
dispatch({
type: UPLOAD_FILE,
payload: response.data.secure_url
});
})
.catch(function(err) {
console.log(err);
});
};
};
Here's the error: "Failed to load https://api.cloudinary.com/v1_1/alertsapi/upload: Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response."
I do not understand why I get this despite using the cors middleware.
Finally, an added wrinkle that seems related: This app checks a JWT token every time it loads the top level component:
// Check for token
if (localStorage.jwtToken) {
// Set auth token header auth
setAuthToken(localStorage.jwtToken);
// Decode token and get user info and exp
const decoded = jwt_decode(localStorage.jwtToken);
// Set user and isAuthenticated
store.dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Clear current Profile
store.dispatch(clearCurrentProfile());
// Redirect to login
window.location.href = "/login";
}
}
class App extends Component {
render() {
return (
...
If I remove this check, the uploadFile action works fine. So if nothing else solves the problem, is there a way to bypass this check for just the Cloudinary upload?
Thanks in advance for anyone's help. Let me know if I can provide any additional info about the app that might help.
回答1:
I figured out a solution to the second question. It turned out that the axios request in the uploadFile action included an Authorization header that was set in my authentication check (shown above) by the function setAuthToken(localStorage.jwtToken)
. This is what caused the second error mentioned above. Unfortunately, that function was imported from another file and escaped my attention. Here it is:
const setAuthToken = (token`enter code here`) => {
if (token) {
// Apply to every request
axios.defaults.headers.common["Authorization"] = token;
} else {
// Delete auth header
delete axios.defaults.headers.common["Authorization"];
}
};
The Cloudinary request does not allow this header. To remove the unwanted header I added
delete axios.defaults.headers.common["Authorization"]
right after the return dispatch => {
line in the uploadFile action. This allowed the file to be uploaded successfully, but it also meant that if another axios request directly followed this action, it would not have the Authorization header. In this case, the next action did include an axios request that required the Authorization header, so it was necessary to add it back manually in that action before the request with this:
axios.defaults.headers.common["Authorization"] = localStorage.jwtToken
Second problem solved. I'm still unclear why the addition of my own request to the same api caused a CORS error.
来源:https://stackoverflow.com/questions/51735517/cloudinary-upload-not-allowed-with-my-jwt-auth-method