Tu baza, tu baza, słyszycie mnie?
Nastał ten przełomowy moment w projekcie kiedy dwa jego elementy dowiedziały się o swoim istnieniu. Część frontendowa odebrała wiadomość od części backendowej. Bardzo mnie to cieszy.
Żeby jednak nie było tak optymistycznie to powiem tylko, że wiadomość była na stałe wpisana w serwerze i jest testowa tak jak cały serwer. Jednak mimo wszystko uważam to za przełom ponieważ po 2 miesiącach pracy wysłałem zapytanie z poziomu Reacta.
Serwer
W tym momencie mój serwer jest bardzo prosty. Ma on jednak dwie ważne funkcje – pozwala wejść na dowolną podstronę bez przechodzenia przez stronę główną i umożliwia odebranie zapytania, dla którego zwróci predefiniowany błąd.
Testowy kod wygląda tak:
var express = require('express'); var path = require('path') var server = express(); server.use(express.static(path.resolve(__dirname + '/../web/'))); server.post('/api/users/signin', function(req, res) { return res.status(404).json({ error: true, message: "Dupa" }) }) server.get('/*', function(req, res){ res.sendFile(path.resolve(__dirname + '/../web/index.html')); }); var port = 8080; server.listen(port, function() { console.log('server listening on port ' + port); });
Backend jest napisany w NodeJS z pomocą biblioteki Express, która ułatwia budowanie aplikacji HTTP.
Linia server.use(express.static(path.resolve(__dirname + '/../web/’))); Odpowiada za obsługę statycznych treści. Jeśli na stronie mam jakieś obrazki to będą one szukane w podanym tutaj katalogu.
Następnie jest część odpowiedzialna za odebranie zapytania POST na wpisany adres. Funkcja dla takiego zapytania zwraca response ze statusem 404 i obiektem reprezentującym błąd. Jak widać „dupa-debugging” musi być :P
Później mam część zwracającą nasz główny plik frontendu dla dowolnego adresu w domenie (poza adresami obsłużonymi powyżej). Dzięki temu mogę wpisać w przeglądarce http://localhost/login i od razu przejść na stronę logowania zamiast wchodzić na stronę główną i dopiero wybierać logowanie z menu. Przyśpiesza to testowanie. Będzie też obecne (zapewne w ulepszonej wersji) w ostatecznej aplikacji bo mniej więcej tego wymagają aplikacje SPA.
Na koniec jest wybranie portu na jakim będzie dział serwer i wypisanie w konsoli informacji o jego uruchomieniu.
Uderz serwer, a odpowie
W tym momencie walczę z komunikacją przy logowaniu. Korzystam z tego przykładu: https://github.com/rajaraodv/react-redux-blog/.
W skrócie – doszło sporo typów akcji. Dla samego logowania mam jednak takie trzy:
import axios from 'axios' import * as types from '../action-types' const ROOT_URL = location.href.indexOf('localhost') > 0 ? 'http://localhost:8080/api' : '/api' //.... export function signInUser(formValues) { const request = axios.post(`${ROOT_URL}/users/signin`, formValues) return { type: types.SIGNIN_USER, payload: request } } export function signInUserSuccess(user) { return { type: types.SIGNIN_USER_SUCCESS, payload: user } } export function signInUserFailure(error) { return { type: types.SIGNIN_USER_FAILURE, payload: error } }
Pierwsza rzecz jest taka, że korzystam z biblioteki axios do ajaxowych zapytań.
ROOT_URL to mój bazowy adres, różny dla aplikacji uruchomionej lokalnie i w przyszłości na serwerze.
Najciekawszą z akcji jest ta pierwsza. Tworzy ona zapytanie typu POST do serwera i przekazuje jego odpowiedź do reducera. Przy okazji poznałem kolejny element JavaScriptu. Widzicie ten string z adresem? Tam nie ma apostrofu tylko grawis (tak to nazywa Wikipedia). Dzięki temu mogę w tekście wstawiać bezpośrednio zmienne. W przykładzie powyżej widzicie jak wstawiony jest bazowy URL.
Reducer
Dla powyższych akcji mam również reducer. Tutaj pokażę tylko jego fragment obsługujący te właśnie akcje:
case types.SIGNIN_USER: return {...state, user: null, status: 'signin', error: null, loading: true} case types.SIGNIN_USER_SUCCESS: return {...state, user: action.payload.user, status: 'authenticated', error: null, loading: false} case types.SIGNIN_USER_FAILURE: error = action.payload.data || {message: action.payload.message} return {...state, user: null, status: 'signin', error: error, loading: false}
Dla akcji logowania ustawia on odpowiedni status użytkownika i włącza flagę mówiącą, że dane są w trakcie ładowania (w tym momencie jej nie używam).
Następnie jest przypadek kiedy udało się zalogować. Status użytkownika się zmienia, a jego obiekt jest ustawiany w state.
Ostatni z pokazanych przypadków obsługuje sytuację błędu logowania. Ustawia on wartość zwróconego błędu.
Kontener
Ostatni element, który pokażę to metoda wywoływana przy submitowaniu formularza logowania:
loginUser: function(values, dispatch) { dispatch(commonActions.blockUi()) return dispatch(userActions.signInUser(values)) .then((result) => { if(result.payload && result.payload.status !== 200) { dispatch(userActions.signInUserFailure(result.payload.data)) dispatch(commonActions.unblockUi()) //throw new SubmissionError(result.payload.data.message) } else { sessionStorage.setItem('jwtToken', result.payload.data.token) dispatch(userActions.signInUserSuccess(result.payload.data)) store.dispatch(commonActions.unblockUi()) browserHistory.push('/') } }) }
Wywołuje ona najpierw akcji logowania, a kiedy ta się wykona odpowiedź jest odpowiednio obsługiwana i w zależności od statusu wywoływana jest akcja powodzenia lub błędu logowania.
W tym miejscu się zatrzymałem, walczę z wyświetlaniem zwróconego błędu na ekranie.
Cały aktualny kod (poza serwerem, który mam w tym momencie w złym katalogu) dostępny jest na GitHubie.
Leave a Comment