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