Регистрация пользователя: создание с помощью Node JS, React и Okta, пошаговая инструкция

Components

In React, everything is built upon components. You can think of a component as something that renders a DOM node. A simple React component looks like this:

That was simple. Now, if you wanted to render this component to a page, then all you’d have to do is import React and then call:

And React would render the component to that element.

There are, of course, more things to a React component, such as state. Below is an example of a counter component that starts counting when added to the DOM and stops when removed.

Notice the methods componentWillMount() and componentWillUnmount(). These are component life-cycle methods that will be executed at various points of a component’s life-cycle (in this case, mount and unmount). These methods are usually used for setting up and tearing down a component and is necessary to use because React will error if you try to set the state of a component when it hasn’t been mounted yet.

Also notice this.props.from. The member this.props is a collection of all the properties (inputs) passed to a component. Properties of a component can be set as shown below:

Configuring babel

Since we’ll be using ES6 and JSX, we need to transpile these files into ES5 (for backwards compatibility with non-modern browsers). This is where Babel comes in. Babel can take our ES6/JSX files as input, and convert those to ES5.

To use Babel, start by installing some dependencies:

Now we’ll instruct Babel on how to compile our files, so create a new file named .babelrc and add this code it:

Finally, in order to get Babel to work with Webpack, we need to edit webpack.config.js and add an entry to the module.loaders array, as shown below:

Configuring webpack

Before you get too excited, kill the server and install Webpack so that we can package all of our client-side scripts (we’ll need this organization soon).

Configure Webpack by creating a new file named webpack.config.js and put the code below in it:

What this will do is look in our /src/ directory (that we’ll create shortly) and package all of the scripts and their dependencies under that directory as one module. Then use the file /src/app.js and its exports as the export of that module.

But in order for Express to serve Webpack files, we have to open up server.js and add these lines to the top of it:

Then immediately after the line var app = express(); add:

As I mentioned before, this will allow Webpack to intercept requests and serve our packaged /js/app.js file.

Deploying the react app to aws

This video shows how to setup a production ready web server from scratch on AWS, then deploy the example React Redux app and configure it to run with a real Node.js MongoDB backend api. The tutorial used in the video is available at React Node.js on AWS – How to Deploy a MERN Stack App to Amazon EC2.

Deploying the react app to microsoft azure

For instructions on how to deploy the React app to Azure with a real backend api built with ASP.NET Core and SQL Server see React ASP.NET Core on Azure with SQL Server – How to Deploy a Full Stack App to Microsoft Azure.

Home route

Now when we’ve setup most of our routing. Let’s look at a special route called the HomeRoute. This route itself doesn’t do anything. But acts as a “marker”, to indicate where to redirect to when logging in and logging out.

So in order to specify where we want to end up when we log out, open up app.js and change the:

Into:

Now when logging out, the Stormpath SDK will know that it should redirect to the ‘/’ path. Now, to specify where to redirect when logging out, change the AuthenticatedRoute that we created in the previous step:

So that it looks like:

Notice how the AuthenticatedRoute wraps the HomeRoute. This is used to indicate the authenticated route that we want to redirect to after login.

Importing components

To be able to reference our pages we need to import them. And in order to make importing easy, we’ll put them all together in an index.js file so we only have to import it once. So let’s create a new file named index.js in our pages directory and export all of our pages from it, as shown below:

With this, we’ll only have to do one import in order to have access to all of our pages.

So let’s do that. Open up app.js file and at the top of the file, add the following import statement:

Index page

In our MasterPage notice the property this.props.children. This will contain the components of the child routes that our router match. So if we had a route that looked like:

And we tried to access /hello. The this.props.children array would be populated with a HelloPage component and for that reason that component would be rendered in our master page.

Now imagine the scenario where you try to access /. Without any this.props.children, this would only render your master page but with empty content. This is where IndexRoute comes into play. With an IndexRoute you can specify the component that should be rendered when you hit the path of the master page route (in our case /).

But before we add our IndexRoute to our router, let’s create a new file in our pages directory named IndexPage.js and add the following to it:

Now let’s add our IndexRoute. Open up app.js and inside the tag <Route path=’/’ component={MasterPage}> add your IndexRoute so that it looks like the following:

Index.html and bootstrap

Now, before getting our hands dirty with React, we’ll prepare the entry page for our app. This page will tell the browser what it must load before we initialize React and our application. So create a new directory named build, then within that, put a file named index.html. Our server will serve all of our static files from this folder.

Then within index.html, put the following:

Also, under the build directory, create a new directory named css and download Bootstrap to it. Name the file bootstrap.min.css.

Now in order for our browser to be able to access these files we need to configure them so that they are served through Express. So open up server.js and at the top of the file add:

Then under the line app.use(stormpath.init(app, …)); add:

Jsx and reserved javascript identifiers

Since JSX is JavaScript, there are some caveats that you need to know when working with React. I.e. when setting properties of a React DOM component you cannot use neither for or class since those are considered reserved JavaScript identifiers.

To illustrate the issue, this won’t work:

But this will:

Jsx variables

Variables can easily be interpolated into your JSX DOM using { nameOfVariable }, e.g. as shown below:

Login page

We now have a React application that shows a header with a default page. But we don’t have a login page yet. So let’s create a new file named LoginPage.js and add some content to it:

Notice the LoginForm component. This is all we have to add in order for us to have a fully working form in which people can sign up from.

But before we can use it, we need to open up app.js and add a route for the page in our router. So inside the tag <Route path=’/’ component={MasterPage}> add the following:

In order to be able to access the login page, we need to add this to our menu. So go ahead and open up Header.js and inside the element <ul className=”nav navbar-nav navbar-right”> add the following:

Main react entry file

Path: /src/index.jsx

The root index.jsx file bootstraps the react redux tutorial application by rendering the App component (wrapped in a redux Provider) into the app div element defined in the base index html file above.

The boilerplate application uses a fake / mock backend that stores data in browser local storage, to switch to a real backend api simply remove the fake backend code below the comment // setup fake backend.

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';

import { store } from './_helpers';
import { App } from './App';

// setup fake backend
import { configureFakeBackend } from './_helpers';
configureFakeBackend();

render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('app')
);

Master page

Before we create our pages, we have to set up our router. The router is what determines how we’ll be able to navigate around in our React application. We’ll start by creating a shared root route. This will act as our “master page”. I.e. all routes under this route will all share the same master component (header). So place the code below inside the <Router> tag in app.js so that it looks like this:

As you can see, we have referenced MasterPage. Something that doesn’t exist yet. So let’s go ahead and create that in a new directory that we’ll name pages, in our src folder.

Now create a new file named MasterPage.js and add this code to it:

As you can see, we don’t have a Header component yet, so let’s go and create a new file named Header.js in the same directory with the following content:

React redux app component

Path: /src/App/App.jsx

The app component is the root component for the react tutorial application, it contains the outer html, routes and global alert notification for the example app.

React redux app folder

Path: /src/App

The app folder is for react components and other code that is used only by the app component in the tutorial application.

React redux helpers folder

Path: /src/_helpers

The helpers folder contains all the bits and pieces that don’t fit into other folders but don’t justify having a folder of their own.

React redux home page component

Path: /src/HomePage/HomePage.jsx

React redux login page component

Path: /src/LoginPage/LoginPage.jsx

React redux register page component

Path: /src/RegisterPage/RegisterPage.jsx

React redux register page folder

Path: /src/RegisterPage

The register page folder is for react components and other code that is used only by the register page component in the tutorial application.

React redux services folder

Path: /src/_services

React auth header

Path: /src/_helpers/auth-header.js

React components folder

Path: /src/_components

The _components folder contains shared React components that can be used anywhere in the application.

React private route component

Path: /src/_components/PrivateRoute.jsx

React router history

Path: /src/_helpers/history.js

React tutorial package.json

Path: /package.json

The package.json file contains project configuration information including package dependencies which get installed when you run npm install. Full documentation is available on the npm docs website.

Redux actions folder

Path: /src/_actions

Redux alert action constants

Path: /src/_constants/alert.constants.js

The alert constants object contains the redux alert action types used to display and clear alerts in the react application.

export const alertConstants = {
    SUCCESS: 'ALERT_SUCCESS',
    ERROR: 'ALERT_ERROR',
    CLEAR: 'ALERT_CLEAR'
};

Redux alert action creators

Path: /src/_actions/alert.actions.js

Contains Redux action creators for actions related to alerts / toaster notifications in the application. For example to display a success alert message with the text ‘Registration Successful’ you can call dispatch(alertActions.success(‘Registration successful’));.

I’ve wrapped the action methods in an alertActions object at the top of the file so it’s easy to see all available actions at a glance and simplifies importing them into other files. The implementation details for each action creator are placed in the below functions.

import { alertConstants } from '../_constants';

export const alertActions = {
    success,
    error,
    clear
};

function success(message) {
    return { type: alertConstants.SUCCESS, message };
}

function error(message) {
    return { type: alertConstants.ERROR, message };
}

function clear() {
    return { type: alertConstants.CLEAR };
}

Redux alert reducer

Path: /src/_reducers/alert.reducer.js

The redux alert reducer manages the application state for alerts / toaster notifications, it updates state when an alert action is dispatched from anywhere in the application, for example when an alertConstants.SUCCESS action is dispatched, the reducer updates the alert state to an object with type: ‘alert-success’ and message: action.message.

import { alertConstants } from '../_constants';

export function alert(state = {}, action) {
  switch (action.type) {
    case alertConstants.SUCCESS:
      return {
        type: 'alert-success',
        message: action.message
      };
    case alertConstants.ERROR:
      return {
        type: 'alert-danger',
        message: action.message
      };
    case alertConstants.CLEAR:
      return {};
    default:
      return state
  }
}

Redux authentication reducer

Path: /src/_reducers/authentication.reducer.js

Redux reducers folder

Path: /src/_reducers

The _reducers folder contains all the Redux reducers for the project, each reducer updates a different part of the application state in response to dispatched redux actions.

Redux registration reducer

Path: /src/_reducers/registration.reducer.js

The redux registration reducer manages the registration section of the application state, as you can see there isn’t much to it, on registration request it just sets a registering flag set to true which the RegisterPage uses to show the loading spinner. On register success or failure it clears the registration state.

Redux store

Path: /src/_helpers/store.js

The redux store helper calls createStore() to create the centralized redux state store for the entire react application.

import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import rootReducer from '../_reducers';

const loggerMiddleware = createLogger();

export const store = createStore(
    rootReducer,
    applyMiddleware(
        thunkMiddleware,
        loggerMiddleware
    )
);

Registration page

Now, let’s add a page where people can sign up. We’ll call it RegistrationPage. So create a new file named RegistrationPage.js and put the following content in it:

Setting up our react express.js project

Start by creating a new project directory and a package.json file for it.

Now install Express, the Stormpath module for Express, and Body Parser:

We need a server to host our application, so create a new file named server.js and put the code below in it:

Awesome. Now we can hook that up to a Stormpath Application by creating a new file named stormpath.yml with the following code in it. And yeah, you do have to replace those values in it with your own.

So far so good. Now try the server by running $ node server.js. If everything is set up correctly then you should see:

If you saw that message, you’ve successfully configured your server to talk with Stormpath and expose a REST API for our React application to use.

Summary

As you have seen in this article, React is a really powerful tool and when used together with ES6, JSX, and Stormpath, building apps suddenly becomes fun again.

If there are any parts that you feel unsure of, feel free to check out the example project and use that as a reference implementation. I also enjoyed this post on React’s design – it explains in beautiful detail why React is awesome.

And if you have questions regarding the Stormpath React SDK, be sure to check out its API documentation.

Virtual dom

Instead of working directly against the DOM, in React all components are kept in their own virtual DOM. You can think of the virtual DOM as a DOM implementation in JavaScript (because it actually is). This virtual DOM is then mapped to a real DOM element.

What this means is that you never have to think of manually manipulating DOM elements again. All you have to do is tell React how you want your component to look like, and it will take care of transforming the DOM the ways necessary (with minimal effort).

Добавление возможностей создания и редактирования публикацию

Подробно рассмотренному ранее редактированию пользователей, можно создать и функциональность добавления и редактирования публикаций. Для это применим знакомый <EditGuesser>, чтобы получить структуру компонента, а затем на её основе создадим пользовательский компонент и, при необходимости, внесем в него коррективы.

Добавляем аутентификацию в приложение reactjs

После создания приложения необходимо добавить аутентификацию через Okta. Для этого нужно добавить пару npm зависимостей. Из папки client запустите:

Если вы используете пакетный менеджер yarn, то:

В папку client/src добавьте файл app.config.js. Код файла:

Настройте файл index.js на использование React Router и Okta React SDK. Завершенный файл index.js будет выглядеть так:

После завершения вы получите компонент BrowserRouter (или Router) из React Router и компонент Security из Okta React SDK. Файл app.config.js импортируется как config, поэтому можно использовать значения конфига в свойствах, необходимых для компонента Security.

Передав заданные значения, вы также окружили компонент App компонентами Router и Security. Метод onAuthRequired просто говорит Okta React SDK, что когда кто-то пытается получить доступ к безопасному роуту без авторизации, его необходимо перенаправлять на страницу авторизации.

Добавляем роуты в react app

Сперва добавьте компонент навигации для будущих роутов. В client/src/components создайте папку shared. Здесь будут храниться все компоненты, используемые в нескольких местах в приложении. В этой папке создайте файл Navigation.js. Файл будет хранить базовый компонент со ссылками на все страницы приложения.

Компонент навигации необходимо обернуть в компонент более высокого порядка withAuth. Это позволит проверять аутентификацию пользователя и показывать кнопки входа или выхода.

Теперь когда есть компоненты для обработки всех роутов создайте роуты. Обновите файл App.js. Конечная версия:

Здесь стоит отметить пару моментов. Импорт SecureRoute и ImplicitCallback компонентов из Okta React SDK. Компонент ImplicitCallback обрабатывает колбек из потока аутентификации и проверяет, есть ли конечная точка внутри приложения React для отлова обратного вызова из Okta.

Компонент Route из React Routeк делает ровно то, что вы думаете: он принимает путь, на который перешел пользователь, и устанавливает компонент для обработки этого роута. SecureRoute проводит дополнительные проверки авторизации пользователя, прежде чем разрешить доступ к роуту.

Из странностей здесь только роут для пути авторизации. Вместо простой установки компонента для обработки пути он запускает метод render, который рендерит компонент LoginPage и задает baseUrl из настроек.

Добавляем страницы в reactjs app

Прежде чем добавлять роуты в React app, создайте пару компонентов для обработки добавляемых роутов.

Добавьте папку components в client/src. Здесь будут храниться все ваши компоненты, это простейший способ их организации. Затем создайте папку home для компонентов домашней страницы. Сейчас компонент домашней страницы будет всего один, но в будущем их может быть больше. Добавьте в папку файл HomePage.js:

Пока что, это все, что нужно для домашней страницы. Самое важное – сделать компонент HomePage классом. Сейчас в нем всего один тег h1, но он будет страницей. То есть в нем будут другие компоненты, поэтому важно, чтобы он был компонентом-контейнером.

Далее создайте папку auth в components. Здесь будут храниться все компоненты, связанные с аутентификацией. В этой папке создайте файл LoginForm.js.

Первое, что нужно отметить – для оборачивания всей формы авторизации вы будете использовать withAuth, компонент более высокого порядка из Okta React SDK. Это добавит в компонент свойство auth, что позволит обращаться к функциям isAuthenticated и redirect компонента более высокого порядка.

Код компонента LoginForm:

Также стоит обратить внимание на импорт библиотеки OktaAuth. Это базовая библиотека для авторизации через Okta приложение, созданное ранее. Обратите внимание, что в конструкторе создался объект OktaAuth и получил переданное свойство baseUrl. Это URL для издателя, который находится в файле app.config.js.

Компонент LoginForm должен содержать другой компонент. Поэтому нужно создать файл LoginPage.js для хранения компонента. Нужно еще раз использовать компонент более высокого порядка withAuth, чтобы получить доступ к функции isAuthenticated. Контент файла LoginPage.js:

Кода меньше, чем в компоненте формы авторизации, но тут есть важные части, которые нужно озвучить.

Еще раз используется компонент более высокого порядка withAuth. Это будет повторяющаяся тема для всех компонентов, которым нужна аутентификация Okta или процесс авторизации. В этом случае он в основном используется для получения функции isAuthenticated.

Когда isAuthenticated возвращает true, значение устанавливается в состояние компонента. Далее идет проверка в методе render, чтобы определить, показывать ли компонент LoginForm или перенаправлять на страницу профиля пользователя (этот компонент мы создадим далее).

Создайте компонент ProfilePage.js в папке auth. Код файла:

Структура базового приложения

Перейдите в папку приложения и создайте новую папку:

Будет создано 2 папки в папке MembershipSample: api и client с Node JS и Express приложением в папке api и базовым приложением React в папке client. Структура папок теперь выглядит следующим образом:

MembershipSample

—api

—client

Для упрощения откройте 2 терминала или 2 вкладки в терминале. Один терминал для папки api приложения express и другой для папки client приложения React.

По умолчанию, приложения React и Node запускаются на порту 3000. Вам понадобится API для запуска на другом порту и прокси-сервер в клиентском приложении.

В папке api откройте файл /bin/www и измените порт, на котором будет запускаться API на 3001.

Затем настройте прокси-сервер для API в клиентском приложении, чтобы можно было вызывать /api/{resource} и прогонять его с порта 3000 на 3001. В файле client/package.json добавьте настройку proxy под name:

Не забудьте запустить npm install или yarn install для всех подпапок (api и client) для установки изменений.

Теперь можно запустить оба приложения с помощью команд running npm start или yarn start в соответствующей папке для API и клиентского приложения.

Установка зависимостей node и react

Для настройки базового приложения необходимо установить следующие инструменты:

Также понадобится профиль разработчика Okta. Для установки Node и npm следуйте инструкциям для своей ОС //nodejs.org/en/. Затем просто установите 2 npm пакета с помощью команды:

Теперь можно настраивать структуру базового приложения.

React fake / mock backend

Path: /src/_helpers/fake-backend.js

The fake backend is used for running the tutorial example without a server api (backend-less). It monkey patches the fetch() function to intercept certain api requests and mimic the behaviour of a real api by managing data in browser local storage. Any requests that aren’t intercepted get passed through to the real fetch() function.

Installing react dependencies

Now when we are acquainted with React, we’ll kick things off by installing some React dependencies:

Before we start coding, we need a place to put our React files, so create a new directory named src, and then use that as your working directory.

Now, let’s start with the entry point of our app. This will be the place where we will set up our React application and its routing. So create a new file named app.js and enter this code:

So now we have a foundation for our application. Let’s go ahead and import the Stormpath SDK and some things we’ll need in it. At the top of your app.js file, add the import statement:

As you can see in app.js there’s now two conflicting Router imports. Since ReactStormpath.Router extends from ReactRouter.Router we won’t be needing that anymore. So go ahead and remove the Router import from react-router. Important: Leave the other ReactRouter imports, we’ll be needing those later.

Now, we’ll initialize the Stormpath SDK. Add the following line right above ReactDOM.render().

That was easy! We’re now ready to start building our pages.

Добавляем api endpoints в node app

Вы, возможно, помните, что Node API проводит регистрацию. Поэтому для обработки этого запроса необходимо добавить endpoint в Node app. Для этого необходимо добавить Okta Node SDK. Из папки api запустите:

Похожее:  Что такое логин при регистрации на сайте ржд и пароль

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *