To read more articles in this series please visit myMaking a blog, this article sample code please visithere.

Demo Running Mode

Download the sample code here and run the following command from your terminal to start Demo:

  1. Install dependencies
npm install
Copy the code
  1. Start the Demo
npm start
Copy the code

The server side of the sample code, implemented by Koa.

Since Google Chrome does not have this problem, please test it by opening http://localhost:8080/ in firefox.

Problem description

The scene that went wrong

  1. The user clicks the pay button.

  2. The front end makes an AJAX request to the server.

  3. The server returns a form generated by Alipay with the address of the payment page and related parameters.

  4. The front end uses the parameters of the form to open the payment page of Alipay in a new window.

In Firefox, this action is blocked by the browser, as shown below:

The bottom line is that any jump triggered by JavaScript after AJAX is blocked by Firefox.

Scenarios that open normally

Of course, Firefox doesn’t block all scenarios, such as these:

  1. After making the AJAX request, open the link in the current window.

  2. After the user clicks directly, the link opens in a new window.

Front-end sample code

// A new window can be opened by direct clicking. // Open with the A TAB and submit the form. document .querySelector('#open')
  .addEventListener('click'.function() { window.open(action); }); // The new window can still be opened after the timer is delayed. document .querySelector('#open')
  .addEventListener('click'.function () {
    setTimeout(() => {
      window.open(action);
    }, 100);
  });
Copy the code

Failed solutions

To list several failed solutions, click the corresponding button at http://localhost:8080/ to see the results.

Front-end sample code

  1. window.open
// Get the alipay payment form asyncfunction getFormStr() {
  const response = await fetch('/getFormStr');
  const result = await response.json();

  document.querySelector('#formWrapper').innerHTML = result.formStr;

  const action = document
    .querySelector('#formWrapper form')
    .getAttribute('action');

  returnaction; } // Method 1: window.open document.querySelector ('#method1')
  .addEventListener('click', async function () {
    const action = await getFormStr();

    window.open(action);
  });
Copy the code
  1. form.submit
// Method 2: form.submit document.querySelector ('#method2')
  .addEventListener('click', async function () {
    const action = await getFormStr();
    let formEle = document.createElement('form');

    formEle.style.display = 'none';
    formEle.method = 'post';
    formEle.target = '_blank';
    formEle.action = action;

    document.body.appendChild(formEle);

    formEle.submit();
  });
Copy the code
  1. Open label A
// method 3: open document. querySelector('#method3')
  .addEventListener('click', async function () {
    const action = await getFormStr();
    let anchorEle = document.createElement('a');

    anchorEle.href = action;
    anchorEle.target = '_blank';

    document.body.appendChild(anchorEle);

    anchorEle.click();
  });
Copy the code

Server side sample code

// Get the form of Alipay router.get('/getFormStr', async (ctx, next) => {
  const response = await fetch(
    'http://rap2.taobao.org:38080/app/mock/250475/getFormStr',); const result = await response.json(); ctx.body = result; await next(); });Copy the code

Successful solutions

The solution to this problem is as follows:

  1. Use the form form in the front end and open a new window to submit parameters.

  2. After processing by the server, it is directly redirected to the payment page of Alipay.

Sample front-end code:

  1. Create a form on the page to store the data.
<button id="method4"</button> <form id="redirectForm" action="/redirectForm" target="_blank"></form>
Copy the code
  1. After clicking the button, a new window opens and the form is submitted.
// Method 4: server redirect document. querySelector('#method4')
  .addEventListener('click', async function () {
    document.querySelector('#redirectForm').submit();
  });
Copy the code

Example server code is as follows:

// Get the alipay payment form and redirect to the Alipay payment page router.get('/redirectForm', async (ctx, next) => {
  const response = await fetch(
    'http://rap2.taobao.org:38080/app/mock/250475/getFormStr',); const result = await response.json(); Const decodedFormStr = he.decode(result.formStr, {isAttributeValue:true}); const decodedFormStrArr = decodedFormStr.split('\n'); Const aliPayUrl = 'const aliPayUrl =${decodedFormStrArr[0].match(/action="([\s|\S]*)">/)[1]}&${
    decodedFormStrArr[1].match(/name="([\s|\S]*)" value="[1]} = /)${decodedFormStrArr[1].match(/value="([\s|\S]*)">/)[1]}`;

  ctx.response.redirect(aliPayUrl);
  await next();
});
Copy the code

The Entity of the conversion

Since Alipay returns the form, there is a “. The Entity character is correctly recognized by the browser as “Entity” if written directly to the page from the form.

If you want to see the full list of Entities, you can click here.

However, it can only be treated as a string on the server, so it needs to use he library for conversion to avoid error of parameter concatenation, as follows:

Const decodedFormStr = he.decode(result.formStr, {isAttributeValue:true});Copy the code

summary

  1. Nowadays, the front-end capability is becoming stronger and stronger, but it still has its limitations. When solving problems, we should not limit our thinking to the front-end domain, but make full use of the resources of the server side.

  2. Entity, though rarely used, can be converted through the HE library when encountered.