使用 React Native 在获取请求中禁用重定向

Alv*_*eda 5 redirect fetch react-native

我正在尝试使用没有 API 的 React Native 来爬网。它是用 PHP 编写的。

要登录用户,必须发送 POST 请求。响应返回一个带有 PHPSessid cookie 的 cookie,我必须捕获它以在后续请求中使用。

我想捕获 cookie 值,购买 POST 响应是 302 并且重定向是自动跟随的,所以我看不到 cookie。在节点中,我可以使用redirect:manual,但它在本机反应中不起作用。

cookie 在后续请求中自动发送,我正在尝试使用 react-native-cookie 手动管理 cookie,我想知道是否可能。

您知道停止重定向的方法吗?

Alv*_*eda 0

我一直在检查代码,我所做的如下:

  • 清除所有cookie
  • 启动空登录请求
  • 捕获 PHPSessID cookie
  • 使用该 PHPSessID 启动登录请求
  • 之后,后续的提取请求将自动具有包含有效登录用户的 PHPSessID cookie,因此我们可以通过简单的提取来使用该网站

这里有一些代码,但重要的是您执行第一个空登录请求,捕获 PHPSessid 并使用该 PHPSessid 启动真正的登录请求。

这将是主要功能:

import Cookie from 'react-native-cookie';
  // I think this is used only to clear the cookies

function login(user, pass){

    // clear all cookies for all domains
    // We need to start withouth authorization token
    Cookie.clear();

    const makeLoginRequest = (sessid) => 
             makeLoginRequestForUserAndPass(user,pass,sessid);

    return makeInitialRequest()
        .then(getSessionIDFromResponse)
        .then(makeLoginRequest)
        .then(checkIfLoggedAndGetSessionID);
}
Run Code Online (Sandbox Code Playgroud)

初始请求是对登录脚本的请求。请注意,我使用 GET 因为它适用于我的网站,也许需要一个空帖子:

function makeInitialRequest() {
    const INIT_PATH = '/index.php?r=site/login';
    const INIT_URL = site + INIT_PATH;
    const request = new Request(INIT_URL, options....);

    return fetch(request);
}
Run Code Online (Sandbox Code Playgroud)

我们在响应中得到了会话 ID。我使用一个简单的正则表达式来提取它。请注意,我们尚未登录;PHP 已经创建了一个会话,这就是我们这里的内容:

function getSessionIDFromResponse(response) {
    return getPHPSessIdFromCookie(response.headers.get('set-cookie'));
}

function getPHPSessIdFromCookie(header) {
    const regex = /PHPSESSID=(\w*)/;
    const match = regex.exec(header);
    return match ? match[1] : '';
}
Run Code Online (Sandbox Code Playgroud)

现在是登录请求。请注意,我无法在这里停止重定向,但我不必这样做,因为我们稍后可以拥有 PHPSessid。重定向必须在 POST 请求中设置为手动:

function makeLoginRequestForUserAndPass(user, pass, sessid) {
    const request = buildLoginRequest(user, pass, sessid);
    return fetch(request);
}

// This is where we build the real login request
function buildLoginRequest(user, pass, sessid) {
    const LOGIN_PATH = '/index.php?r=site/login';
    const LOGIN_URL = site + LOGIN_PATH;

    const fields = [
        {name: 'LoginForm[username]', value: user},
        {name: 'LoginForm[password]', value: pass},
        etc...
    ];
    const data = translateFieldsToURLEncodedData(fields);

    const headers = {
        'Content-type': 'application/x-www-form-urlencoded',
        Cookie: `PHPSESSID=${sessid}`, // HERE is where you put the data
    };

    const options = { method: 'POST',
        headers: headers,
        mode: 'cors',
        cache: 'default',
        agent: proxy,
        body: data,
        redirect: 'manual'  // VERY IMPORTANT: if you don't do it, the cookie is lost
    };

    return new Request(LOGIN_URL, options);
}

// Simple utility function
function translateFieldsToURLEncodedData(fields){
    let pairs = fields.map( (field) => {
        return encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value);
    });
    return pairs.join('&');
}
Run Code Online (Sandbox Code Playgroud)

这是最后一部分。为了查看我是否已登录,我检查了响应中是否包含属于登录错误页面的文本。我还得到了 PHPSessid (我认为它在登录后发生了变化,不确定,那是一年前的事)但我不知道我是否使用过它,我相信它会自动包含在后续请求中。我认为这部分可以简化和改进:

function checkIfLoggedAndGetSessionID(response) {
    return (
        checkIfLoggedOK(response)
            .then(() => getSessionIDFromResponse(response))
    );
}

function checkIfLoggedOK(response){
    return getTextFromResponse(response)
        .then(throwErrorIfNotLogedOk);
}

function getTextFromResponse(response) {
    return response.text();
}

function throwErrorIfNotLogedOk(page) {
    if(isErrorPage(page)) throw new Error("Login failed");
}

function isErrorPage(text) {
    const ERROR_MESSAGE = 'Something that appears in login failed page of your site';
    let n = text.search(ERROR_MESSAGE);
    return n !== -1;
}
Run Code Online (Sandbox Code Playgroud)

希望这会有用。