在使用React Native时禁用fetch请求中的重定向

6
我正在尝试使用React Native爬取没有API的网站,该网站用PHP编写。为了登录一个用户,必须发送POST请求。响应会返回一个cookie和一个PHPSessid cookie,我必须捕获该cookie以在后续请求中使用。
我想捕获cookie值,但是POST响应是302重定向,并且自动跟随重定向,所以我看不到cookie。在node中,我能够使用"redirect:manual"来实现,但在React Native中这种方法行不通。
cookie会自动发送到后续请求中,但是我正在尝试手动管理cookie,使用react-native-cookie,我想知道是否可能。
您知道停止重定向的方法吗?

1
你成功解决了这个问题吗? - Andrew
@Alvaro - 你找到解决方案了吗? - Nick
我刚刚发布了我所做的事情。 - Alvaro Maceda
1个回答

0

我一直在检查代码,我的操作如下:

  • 清除所有的cookies
  • 发起一个空的登录请求
  • 捕获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);
}

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

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);
}

在响应中,我们有会话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] : '';
}

现在是登录请求。请注意,我无法在这里停止重定向,但是必须这样做,因为我们稍后可能会有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('&');
}

这是最后一部分。为了确认我是否已登录,我检查了响应中是否存在属于登录错误页面的文本。我还获得了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;
}

希望这对你有用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接