dentalhero
dentalhero

Reputation: 699

Express.js Access to Fetch from Origin Blocked by CORS Policy

I am posting data from a simple html form to an Express.js endpoint. When I post the data, I received the following error:

"Access to fetch at 'https://samplesite.net/sample' from origin 'https://samplesite.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."

Here's the form code:

<form id="form-email-list" >
       <div class="contact__fields">
         
          <div class="field field--with-error">
             <input class="field__input" 
                autocomplete="given-name" 
                type="text" 
                id="form-email-firstname" 
                name="form_email_firstname" 
                value="" 
                placeholder="{{ 'templates.email_newsletter.form.first_name' | t }}" 
                autocapitalize="words"
                autofocus
                data-parsley-validation-threshold="2"
                data-parsley-error-message="Enter your first name"
                data-parsley-required
                />
             <label class="field__label" for="form-email-firstname">{{ 'templates.email_newsletter.form.first_name' | t }} <span class="req" aria-hidden="true">*</span></label>
          </div>
        
          <div class="field">
             <input
                class="field__input"
                autocomplete="family-name" 
                type="text" 
                id="form-email-lastname" 
                name="form_email_lastname"
                value=""
                placeholder="{{ 'templates.email_newsletter.form.last_name' | t }}"
                autocapitalize="words"
                data-parsley-validation-threshold="2"
                data-parsley-error-message="Enter your last name"
                data-parsley-required
                />
             <label class="field__label" for="form-email-lastname">{{ 'templates.email_newsletter.form.last_name' | t }} <span class="req" aria-hidden="true">*</span></label>
          </div>
        
          <div class="field">
             <input
                class="field__input"
                type="email"
                id="form-email-email"
                name="form_email_email"
                value=""
                placeholder="{{ 'templates.email_newsletter.form.email' | t }}"
                autocorrect="off"
                autocapitalize="off"
                autocomplete="email"
                data-parsley-validation-threshold="3"
                data-parsley-type="email"
                data-parsley-error-message="Enter a valid email address"
                data-parsley-required
                />
             <label class="field__label" for="form-email-email">{{ 'templates.email_newsletter.form.email' | t }} <span class="req" aria-hidden="true">*</span></label>
          </div>
         
          <div class="field">
             <input 
                class="field__input"
                autocomplete="postal-code"
                type="text"
                id="form-email-zip"
                name="form_email_zip"
                value=""
                placeholder="{{ 'templates.email_newsletter.form.zip' | t }}"
                autocomplete="postal-code"
                data-parsley-pattern="^[0-9]{5}(?:-[0-9]{4})?$"
                data-parsley-maxlength="10"
                data-parsley-validation-threshold="5"
                data-parsley-error-message="Enter a 5 (XXXXX) or 9 (XXXXX-XXXX) digit zip code"
                data-parsley-required
                />
             <label class="field__label" for="form-email-zip">{{ 'templates.email_newsletter.form.zip' | t }} <span class="req" aria-hidden="true">*</span></label>
          </div>
       </div>
    
       <div class="field">
          <input
             class="field__input js-validate-dob"
             autocomplete="bday"
             type="date"
             id="form-email-dob" 
             name="form_email_dob"
             value=""
             placeholder="MM/DD/YYYY"
             data-date-format="MM/DD/YYYY"
             data-date-maxDate=""
             data-parsley-maxdate=""
             data-parsley-required
             />
          <label class="field__label" for="form-email-dob">{{ 'templates.email_newsletter.form.dob' | t }} <span class="req" aria-hidden="true">*</span></label>
       </div>
    
       <div class="field checkbox">
          <input
             autocomplete="off"
             type="checkbox"
             id="form-email-optin"
             name="form_email_optin"
             value=""
             />
          <label for="form-email-optin">
          Yes, I consent...yadda yadda
          </label>
       </div>
     
       <div class="contact__button">
          <button type="submit" id="btn-submit" class="button">
          {{ 'templates.email_newsletter.form.send' | t }}
          </button>
       </div>
    
    </form> 

Fetch Code

const formEmailList = document.getElementById('form-email-list');

formEmailList.addEventListener('submit', function(event) {

    event.preventDefault();

    let formEmailListURL = "https://samplesite.net/sample";
    let fieldFirstName = document.getElementById('form-email-firstname').value;
    let fieldLastName = document.getElementById('form-email-lastname').value;
    let fieldEmail = document.getElementById('form-email-email').value;
    let fieldZip = document.getElementById('form-email-zip').value;
    let fieldDOB = document.getElementById('form-email-dob').value;
    let fieldOptIn = document.getElementById('form-email-optin').value;

    let emailListData = {
        abi_first_name: fieldFirstName,
        abi_last_name: fieldLastName,
        abi_email: fieldEmail,
        abi_zipcode: fieldZip,
        abi_dateofbirth: fieldDOB,
        abi_emailoptin: fieldOptIn
    }

    console.log('Posting Form...');

    fetch(formEmailListURL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(emailListData),
        })
        .then(response => {
            if (!response.ok) {
                throw new Error('Server returned ' + response.status);
            }
            return response.json();
        })
        .then(function(data) {
            console.log('Added to List:', data);
        })
        .catch(error => {
            console.error('There was a problem:', error);
        });
});

Here's the Express App Code

const express = require("express");
    const helmet = require("helmet");
    const cors = require("cors");
    const app = express();
    const port = process.env.PORT || 1337;
    const corsOptions = {
        origin: 'https://samplesite.com',
        optionsSuccessStatus: 200
    }
    
    // Secure Headers
    app.use(helmet());
    
    // parse json request body
    app.use(express.json());
    
    // parse urlencoded request body
    app.use(express.urlencoded({
        extended: true
    }));
    
    // Create Asset Dir
    app.use(express.static(__dirname + "/public"));
    
    // Sample Route
    app.options("/sample", cors(corsOptions));
    app.post("/sample", cors(corsOptions), (req, res, next) => {
    
        const postToEmailList = async () => {
            try {
                const fetch = require('node-fetch');
                const FormData = require('form-data');
                const form = new FormData();
                form.append("targetType", "dataExtension");
                form.append("attributes", `{"campaign":"test","email":"${req.body.email}","test_id":"${req.body.email}","dateofbirth":"${req.body.dateofbirth}","first_name":"${req.body.first_name}","last_name":"${req.body.last_name}","zipcode":"${req.body.zipcode}","tcpp":"yes","emailoptin":"${req.body.emailoptin}"}`);
                form.append("withTriggeredSend", "");
                form.append("isJourneyBuilderIntegrated", "false");
                const res = await fetch("https://test.com", {
                    method: "POST",
                    mode: 'cors',
                    body: form
                });
                const data = await res.text();
                console.log(' data', data);
            } catch (error) {
                console.log(" error");
                console.log(error);
            }
        }
    });
    
    app.get("*", function(req, res, next) {
        let err = new Error("Page Doesn't Exist");
        err.statusCode = 404;
        next(err);
    });
    
    app.listen(port, (err) => {
        if (err) {
            return console.error(err);
        }
        return console.log(`server is listening on ${port}`);
    });
    
    module.exports = app;

Upvotes: 0

Views: 844

Answers (1)

Quentin
Quentin

Reputation: 943569

Response to preflight request doesn't pass access control check

You're making a preflighted request.


 app.post("/sample", cors(corsOptions), (req, res, next) => {

… but you've only enabled CORS for POST requests.

Since the preflight request is an OPTIONS request, it won't get permission.


The documentation for the module you are using has a section about preflight requests which gives a couple of options including just using app.options("/sample", cors(corsOptions)).

Upvotes: 1

Related Questions