• Как реализовать авторизацию пользователя на стеке React Redux Node?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    Пригодится jwt. (гугл)

    В общих словах: вам нужно выдать юзеру токен. С этим токеном юзер будет выполнять запросы к вашим защищенным методам api (вы можете оставить какие-то методы "без токена").

    Node.js часть
    Пример роута на логин (сделано не лучшим образом, в плане коллбэков, но что нашлось под рукой...):

    auth.js
    ...
    const v4 = require('node-uuid').v4
    const jwt = require('jsonwebtoken')
    ...
    router.post('/signin', (req, res, next) => {
    
      // валидация, например... 
    
      if (errors) {
        return res.status(400).json({ errors })
      } else {
        // поиск юзера в базе, сравнение хэша и прочие необходимые операции
        ...
       // допустим все ок, нужно ответить токеном
      // генерируем необходимые опции и сам токен
    
            const payload = {
              _id: user._id,
              iss: 'http://localhost:3000',
              permissions: 'poll',
            }
            const options = {
              expiresIn: '7d',
              jwtid: v4(),
            }
            const secret = new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64')
            jwt.sign(payload, secret, options, (err, token) => {
              // отвечаем токеном, для будущих запросов с клиента
              return res.json({ data: token })
            })
         ...
    })
    
    module.exports = router;


    Пример защищенного роута (метода, который требует токен):
    ...
    const jwt = require('jsonwebtoken')
    const Poll = require('../models/poll')
    ...
    
    const requireToken = (req,res,next) => {
      const token = req.headers['x-api-key']
      const secret = new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64')
    
      jwt.verify(token, secret, (err, decoded) => {
        if (err) {
          return res.status(401).json({ error: err.message })
        }
        req.params.userId = decoded._id
        next()
      })
    }
    ...
    
    // обратите внимание на requireToken - функция вызывается при каждом обращении к роуту
    // то есть при каждом PUT запросе по адресу (например: PUT api/v1/products/1238914)
    // будет вызываться requireToken
    
    router.put('/:id', requireToken, (req, res, next) => {
      const { productId } = req.body
      const userId = req.params.userId
    
      Poll.findById(req.params.id)
        .populate({ path: 'products' })
        .exec((err, poll) => {
            // ... необходимые действия в базе, в процессе успешного зачтения голоса...
        }
    })


    Клиентское приложение:

    Типичный ActionCreator (в качестве библиотеки для запросов можно взять bluebird, isomorphic-fetch, да хоть нативный xhr)
    actions/LoginActions.js

    export function login(data) {
      return dispatch => {
        dispatch({ type: LOGIN_REQUEST })
    
        // request в данном случае - https://github.com/KyleAMathews/superagent-bluebird-promise
        return request.post(`${API_ROOT_V1}/auth/signin`)
          .send(data)
          .then(res => {
            if (!res.ok) {
              dispatch({ type: LOGIN_FAILURE })
            } else {
              dispatch({
                type: LOGIN_SUCCESS,
                data: res.body.data,
              })
              //сохранение токена в localStorage
              localStorage.setItem('cks_token', res.body.data)
            }
          }, err => {
            dispatch({ type: LOGIN_FAILURE })
          })
      }
    }


    Пример голосования с токеном (выше, в node.js части был метод апи, а это PUT запрос с клиента)

    export function vote(productId, voteId) {
      return dispatch => {
        dispatch({ type: POLL_VOTE_REQUEST })
    
        return request.put(`${API_ROOT_V1}/api/v1/vote/${voteId}`)
          .set('X-API-Key', localStorage.getItem('cks_token')) // установка ТОКЕНА в заголовок 'X-API-Key'
          .send({ productId })
          .then(res => {
            if (!res.ok) {
              dispatch({ type: POLL_VOTE_FAILURE })
            } else {
              dispatch({
                type: POLL_VOTE_SUCCESS,
                data: normalize(res.body.data, schema.poll),
              })
            }
          }, err => {
            dispatch({ type: POLL_VOTE_FAILURE })
          })
      }
    }


    Надеюсь код формы с кнопкой "голосовать" не трубется.
    ====

    Все это можно организовать удобнее, например, выставлять токен всем запросам автоматически (речь про клиентскую часть):
    import fetch from 'isomorphic-fetch'
    ...
    const defaultHeaders = {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    }
    
    function buildHeaders() {
      const authToken = localStorage.getItem('TOKEN')
      return { ...defaultHeaders, Authorization: authToken }
    }
    ...
    export function httpPost(url, data) {
      const body = JSON.stringify(data)
    
      return fetch(url, {
        method: 'post',
        headers: buildHeaders(),
        body: body,
      })
      .then(... код оработки запроса ...)
    }


    ===

    Проверить наличие токена, можно с в роутере, с помощью хука на onEnter.

    root.js
    ...
    <Provider store={store}>
          <Router history={routerHistory}>
            {configRoutes(store)} // Роуты создаются функцией, чтобы можно было использовать внутри нее store.getState()
          </Router>
        </Provider>
    ...


    routes.js
    export default function configRoutes(store) {
      function _ensureAuthenticated(nextState, replace, callback) {
        const { dispatch } = store
        const { session } = store.getState()
        const { currentUser } = session
        let nextUrl
    
        if (!currentUser && localStorage.getItem('cks.token')) {
          dispatch(getCurrentAccount())
        } else if (!localStorage.getItem('cks.token')) {
          replace('/signin')
        }
        callback()
      }
    
      return (
        <Route path='/' component={App}>
          <Route path='/signin' component={SigninContainer} />
          <Route path='/signup' component={SignupContainer} />
    
          <Route path='/locked' component={AuthenticatedContainer} onEnter={_ensureAuthenticated}>
            <Route component={LockedArea}>
              <Route path='/locked/a' component={A} />
              <Route path='/locked/b/:id' component={B} />
              <Route path='/locked/c' component={C} />
            </Route>
          </Route>
        </Route>
      )


    Остается только создать AuthenticatedContainer в котором проверять: есть ли currentUser (в рамках этого примера) и если нет - возвращать false. А если есть - this.props.children (в которых будут дальнейшие роуты..)
    ===

    Итого:
    1) у вас есть API написанное на node.js.
    2) У этого API есть защищенные методы, которые проверяют наличия токена в http-запросе. Токен выдается при запросе на логин. Само собой, операция логина (то есть POST запрос на your-api/login) не требует токена.
    3) После удачного логина, вы получаете в ответе на свой запрос токен (это уже внутри клиентского кода, и если мы говорим о redux - то внутри асинхронного вызова в actions)
    4) Сохраняете токен (например, в localStorage)
    5) Во все необходимые http-запросы устанавливаете заголовок с токеном
    Ответ написан
    15 комментариев
  • Как отправить пост запрос через VueJS?

    @last_round Автор вопроса
    Всё, разобрался.
    vueJS:
    @$http.post("includes/register.php?dataform=", @dataform).then((res) -> 
    				console.log res
    			(error) -> 
    				alert 'Error')

    php:
    $json = file_get_contents('php://input'); 
    	$data = json_decode($json, JSON_BIGINT_AS_STRING);
    	print_r($data['password']);
    Ответ написан
    Комментировать
  • Как отследить изменения viewport на JS?

    iiiBird
    @iiiBird
    Пока ты спишь - твой конкурент совершенствуется
    window.onresize = function(event) {
        ...
    };
    Ответ написан
    Комментировать
  • React+Redux VS Backbone (Marionette) в 2017?

    maxfarseer
    @maxfarseer
    https://maxpfrontend.ru, обучаю реакту и компании
    1. Поясните этот момент. Поиск, фильтрация... это же обычные операции на vanila js? Спокойно обрабатывайте ваши данные в reducer'ах, хоть с помощью underscore, хоть с помощью lo-dash...
    2. Разделение кода возможно. Так как за сборку отвечает обычно webpack, начать можно с небольшого теоретического экскурса здесь: Кантор (ру) (гугл, подкидывает еще (англ.))
    3. Не мало всего уже есть, но конечно меньше.

    Направлять вас на путь истинный вряд ли нужно: у вас есть задачи, решайте их так как умеете и зарабатывайте. Появится время для изучения реакта - успеете подучить.
    Ответ написан
    Комментировать
  • React+Redux VS Backbone (Marionette) в 2017?

    AppFA
    @AppFA
    Frontend developer at Yandex
    React это не фреймворк, а лишь либа для view
    1. Никто не запрещает использовать lodash\underscore для работы с данными. Для фильтрации\поиска используйте селекторы.
    2. Используйте webpack для сборки проекта, в настоящее время это единственное рабочее решение, так же в webpack есть асинхронная загрузка модулей - require.ensure, так что вы спокойно можете разбивать свое приложение на чанки и подгружать их в нужный момент.
    3. По-моему сейчас очень, очень много плагинов адаптированных под реакт, за не большую практику работы с этим стеком у меня ни разу не возникло необходимости писать что-то самому с 0, всегда можно найти какое-то решение, форкнуть и допилить под себя.

    По поводу backbone, честно не знаю - на мой взгляд React более лаконичен и на нем можно быстрее начать писать уже готовое приложение + при правильной архитектуре проекта поддержка в будущем будет без боли.
    Ответ написан
    Комментировать
  • React+Redux VS Backbone (Marionette) в 2017?

    @kahi4
    Как бы коллекция – простой объект и обрабатывай их стандартными средствами.


    Только редукс тогда превратится в тыкву. Стоит сразу смотреть на что-то иммутабельное, а там, обычно, вполне достаточный набор для работы с коллекциями. В целом, единственное преимущество бекбоновский коллекций -- функция fetch (ну и встроенные события, но с редуксом они не нужны), которая позволяет сразу с сервера получать списки, абстрагируя внутри себя процесс загрузки. Впрочем, где удобства, там трудности с гибкостью и часто бекбоновский fetch выкидывается в помойку. Ну и опять же -- при определенном уровне упоротости, можно использовать бекбон и редукс одновременно, но это практика, которая ведет вникуда и одно из этого все равно скоро выкинете.

    2. Как говорили выше, webpack. Во вторую версию завезли LazyliLoader, так что там даже делать ничего не нужно ручками. Ну и вебпак достаточно умен, чтобы поделить оптимальным образом на бандлы. И опять же, если есть желание поделать что-то ручками, не создаст никаких проблем сделать это и с обычным RequireJS (впрочем, настоятельно рекомендую его все таки похоронить как архаизм, использовать es6 modules, чем их собирать -- вопрос десятый)

    3. Сейчас под React есть обертки даже над svg, canvas, webGl и даже над three.js. Сложно представить задачу, под которое нет готовых решений, либо которое не пишется за пол часа.

    Как человек, имеющий опыт с первым ангуларом, реактом и бекбоном я бы рекомендовал использовать все таки реакт (из этих трех). Бекбон в свое время был прорывом и до сих пор старается выглядеть молодцом, но его время проходит и сейчас он все таки уступает в скорости разработки и удобства реакту (с флюксом, редуксом или моб-иксом). Конечно, реакт не панацея, но так получилось, что я аж три раза участвовал в переводе проекта с бекбона на реакт и каждый раз это того стоило и все были только счастливы, хотя первое время и побаивались и говорили, что бекбон все таки проверен временем, а реакт -- так, для хипстеров, программирующих с смузи в старбаксе.
    Ответ написан
    2 комментария
  • Как правильно пробрасывать эвенты в react?

    Laiff
    @Laiff
    Front-end developer
    Примерно таким образом:
    class Input {
      render() {
        return <input {...props} />
      }
    }
    
    class MyComponent extends Component {
      constructor(props) {
        super(props);
    
        this.state = {};
      }
    
      handleChange = e => {
        this.setState({e.target.value});
      }
    
      render() {
        return <Input type="text" onChange={this.handleChange} value={this.state.value} />
      }
    }


    И рекомендую прочитать про ValueLink https://facebook.github.io/react/docs/two-way-bind...
    Ответ написан
    Комментировать
  • Underscore Template, как выводить лишь если параметр задан?

    @an23
    <%if(typeof description !== "undefined" ){%>
    <p class="description"> <%= description %> </p>
    <%}%>


    Проверку можешь подогнать под свое условие
    Ответ написан
    Комментировать
  • Зачем в bootstrap отрицательный margin в row ?

    Отрицательный отступ у row для того чтобы при многоуровневой структуре не суммировались отступы у блоков разных уровней.
    <div class="row">
        <div class="col-sm-12" id="outer">
            <div class="row">
                <div class="col-sm-12" id="inner">
                </div>
            </div>
        </div>
    </div>

    В приведенном примере блок #inner будет по размеру такой же как блок #outer. Если бы у row не было отрицательных отступов, то блок #inner был бы меньше блока #outer на 15px с каждой стороны.
    Ответ написан
    Комментировать