带有Express后端的Create-react-app在本地工作,但是在部署到heroku时路由不正确

时间:2018-09-07 09:35:49

标签: reactjs express heroku spotify create-react-app

我关注了这篇文章,介绍了如何使用带有快速后端和spotify api的create-react-app创建应用程序,并且从那时起就一直在尝试将其部署到heroku: https://medium.com/@jonnykalambay/now-playing-using-spotifys-awesome-api-with-react-7db8173a7b13

一切似乎在本地都可以正常工作,授权流程很好,一旦完成,express将提供一个静态index.html,其中包含一个在客户端具有div且带有“ root” ID的div,而index.js则将react'App ”。

但是,当将应用程序部署到heroku时,服务器端进程仍然有效,授权和重定向成功,但是index.html express服务是服务器端的上端服务器(在项目的根“ public”目录中) ),其中没有运行任何React脚本。 即使试图通过在此index.html中添加react,babel,index.js脚本和“ root” div来“强制”脚本运行,

可以在https://github.com/Johnnybar/spot.stats上找到Github仓库,并在https://spot-stats.herokuapp.com/上找到该应用程序

感觉这里缺少一些简单的东西,但是在尝试破解一段时间的部署方式后,我真的很感谢任何提示,谢谢!

app.js(应用程序根目录/,授权流程,在heroku和本地均成功)

/**
 * https://developer.spotify.com/web-api/authorization-guide/#authorization_code_flow
 */

var express = require('express'); // Express web server framework
var request = require('request'); // "Request" library
var querystring = require('querystring');
var cookieParser = require('cookie-parser');
let secrets;
let client_id;
let client_secret;
let redirect_uri;
let client_url;
if (process.env.NODE_ENV != 'production') {
    secrets = require('./secrets.json');
    client_id = secrets.client_id;
    client_secret = secrets.client_secret;
    redirect_uri = secrets.redirect_uri;
    client_url = 'http://localhost:3000/#';
}
else{
    client_id =   process.env.CLIENT_ID;
    client_secret =  process.env.CLIENT_SECRET;
    redirect_uri = process.env.REDIRECT_URI; 
    client_url = 'https://spot-stats.herokuapp.com/#';
}
/**
 * Generates a random string containing numbers and letters
 * @param  {number} length The length of the string
 * @return {string} The generated string
 */
var generateRandomString = function(length) {
    var text = '';
    var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    for (var i = 0; i < length; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return text;
};

var stateKey = 'spotify_auth_state';

var app = express();

app.use(express.static(__dirname + '/public'))
    .use(cookieParser());

app.get('/login', function(req, res) {

    var state = generateRandomString(16);      
    res.cookie(stateKey, state);

    // application requests authorization
    var scope = 'user-read-private user-read-email user-read-playback-state user-top-read';
    res.redirect('https://accounts.spotify.com/authorize?' +
    querystring.stringify({
        response_type: 'code',
        client_id: client_id,
        scope: scope,
        redirect_uri: redirect_uri,
        state: state
    }));
});

app.get('/callback', function(req, res) {

    // application requests refresh and access tokens
    // after checking the state parameter
    var code = req.query.code || null;
    var state = req.query.state || null;
    var storedState = req.cookies ? req.cookies[stateKey] : null;
    // console.log('here storedstate: ', storedState);

    if (state === null || state !== storedState) {
        res.redirect('/#' +
      querystring.stringify({
          error: 'state_mismatch'
      }));
    } else {
        res.clearCookie(stateKey);
        var authOptions = {
            url: 'https://accounts.spotify.com/api/token',
            form: {
                code: code,
                redirect_uri: redirect_uri,
                grant_type: 'authorization_code'
            },
            headers: {
                'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
            },
            json: true
        };

        request.post(authOptions, function(error, response, body) {
            if (!error && response.statusCode === 200) {

                var access_token = body.access_token,
                    refresh_token = body.refresh_token;

                var options = {
                    url: 'https://api.spotify.com/v1/me',
                    headers: { 'Authorization': 'Bearer ' + access_token },
                    json: true
                };

                // use the access token to access the Spotify Web API
                request.get(options, function(error, response, body) {
                    console.log(body);
                });

                // we can also pass the token to the browser to make requests from there
                res.redirect(client_url +
          querystring.stringify({
              access_token: access_token,
              refresh_token: refresh_token
          }));
            } else {
                res.redirect('/#' +
          querystring.stringify({
              error: 'invalid_token'
          }));
            }
        });
    }
});

app.get('/refresh_token', function(req, res) {

    // requesting access token from refresh token
    var refresh_token = req.query.refresh_token;
    var authOptions = {
        url: 'https://accounts.spotify.com/api/token',
        headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
        form: {
            grant_type: 'refresh_token',
            refresh_token: refresh_token
        },
        json: true
    };

    request.post(authOptions, function(error, response, body) {
        if (!error && response.statusCode === 200) {
            var access_token = body.access_token;
            res.send({
                'access_token': access_token
            });
        }
    });
});

console.log('Listening on 5000');
app.listen(process.env.PORT || 5000);

index.html(应用程序根目录/公共,在成功通过身份验证后显示在heroku上-这不是所需的index.html)

<!doctype html>
<html>
  <head>
    <title>Authorization Code flow with Spotify</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <style type="text/css">
      #login, #loggedin {
        display: none;
      }
      .text-overflow {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        width: 500px;
      }
    </style>
  </head>

  <body>
    <div class="container">
      <div id="login">

        <a href="/login" class="btn btn-primary">Log in with Spotify</a>
      </div>
      <div id="loggedin">
        <div id="user-profile">
        </div>
        <div id="oauth">
        </div>
        <button class="btn btn-default" id="obtain-new-token">Obtain new token using the refresh token</button>
      </div>
    </div>

    <script id="user-profile-template" type="text/x-handlebars-template">
      <h1>Logged in as {{display_name}}</h1>
      <div class="media">
        <div class="pull-left">
          <img class="media-object" width="150" src="{{images.0.url}}" />
        </div>
        <div class="media-body">
          <dl class="dl-horizontal">
            <dt>Display name</dt><dd class="clearfix">{{display_name}}</dd>
            <dt>Id</dt><dd>{{id}}</dd>
            <dt>Email</dt><dd>{{email}}</dd>
            <dt>Spotify URI</dt><dd><a href="{{external_urls.spotify}}">{{external_urls.spotify}}</a></dd>
            <dt>Link</dt><dd><a href="{{href}}">{{href}}</a></dd>
            <dt>Profile Image</dt><dd class="clearfix"><a href="{{images.0.url}}">{{images.0.url}}</a></dd>
            <dt>Country</dt><dd>{{country}}</dd>
          </dl>
        </div>
      </div>
    </script>

    <script id="oauth-template" type="text/x-handlebars-template">
      <h2>oAuth info</h2>
      <dl class="dl-horizontal">
        <dt>Access token</dt><dd class="text-overflow">{{access_token}}</dd>
        <dt>Refresh token</dt><dd class="text-overflow">{{refresh_token}}</dd>
      </dl>
    </script>

    <script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0-alpha.1/handlebars.min.js"></script>
    <script>
      (function() {

        /**
         * Obtains parameters from the hash of the URL
         * @return Object
         */
        function getHashParams() {
          var hashParams = {};
          var e, r = /([^&;=]+)=?([^&;]*)/g,
              q = window.location.hash.substring(1);
          while ( e = r.exec(q)) {
             hashParams[e[1]] = decodeURIComponent(e[2]);
          }
          return hashParams;
        }

        var userProfileSource = document.getElementById('user-profile-template').innerHTML,
            userProfileTemplate = Handlebars.compile(userProfileSource),
            userProfilePlaceholder = document.getElementById('user-profile');

        var oauthSource = document.getElementById('oauth-template').innerHTML,
            oauthTemplate = Handlebars.compile(oauthSource),
            oauthPlaceholder = document.getElementById('oauth');

        var params = getHashParams();

        var access_token = params.access_token,
            refresh_token = params.refresh_token,
            error = params.error;

        if (error) {
          alert('There was an error during the authentication');
        } else {
          if (access_token) {
            // render oauth info
            oauthPlaceholder.innerHTML = oauthTemplate({
              access_token: access_token,
              refresh_token: refresh_token
            });

            $.ajax({
                url: 'https://api.spotify.com/v1/me',
                headers: {
                  'Authorization': 'Bearer ' + access_token
                },
                success: function(response) {
                  userProfilePlaceholder.innerHTML = userProfileTemplate(response);

                  $('#login').hide();
                  $('#loggedin').show();
                }
            });
          } else {
              // render initial screen
              $('#login').show();
              $('#loggedin').hide();
          }

          document.getElementById('obtain-new-token').addEventListener('click', function() {
            $.ajax({
              url: '/refresh_token',
              data: {
                'refresh_token': refresh_token
              }
            }).done(function(data) {
              access_token = data.access_token;
              oauthPlaceholder.innerHTML = oauthTemplate({
                access_token: access_token,
                refresh_token: refresh_token
              });
            });
          }, false);
        }
      })();
    </script>
  </body>
</html>

index.html(应用程序根目录/客户端/公共,在经过身份验证后在本地工作,并在app / client / src中呈现App组件。这是所需的index.html)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>Spotify app</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

index.js(app-root / client / src)

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

package.json脚本(app-root /)

"scripts": {
    "start": "concurrently \"cd client && npm start\" \"node app.js\"",
    "heroku-postbuild": "cd client && npm install && npm run build",
    "push": "concurrently \"git push heroku master\" \"git push origin master\"",
    "app": "concurrently \"cd client && npm start\" \"node app.js\""
  },

package.json脚本(应用程序根/客户端)

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },

1 个答案:

答案 0 :(得分:0)

我建议您使用webpackbrowserify之类的捆绑器,以便将react中的所有内容捆绑到一个js文件中。该js文件将由node提供,当您部署到heroku时,您可以在html中访问它。我已经将它们用于相同的目的,并且有效。

如果您使用webpack,则必须将其添加到package.json中的scripts

"postinstall": "webpack -p",

此外,我似乎对procfile具有不同的配置,您也可以尝试以下配置:

web: node app.js