«Я люблю писать код аутентификации и авторизации». Нет Java-разработчика. Надоело строить одни и те же экраны входа снова и снова? Попробуйте API Okta для размещенной аутентификации, авторизации и многофакторной аутентификации.
За последние пару лет React получил много положительных отзывов, что делает его привлекательным вариантом для разработчиков Java! Как только вы узнаете, как это работает, у вас появится много смысла, и с ним будет весело развиваться. Не только это, но это быстро злой! Если вы следили за мной или читали этот блог некоторое время, вы, возможно, помните мое руководство по Bootiful Development с Spring Boot и Angular . Сегодня я покажу вам, как создать такое же приложение, кроме как с React на этот раз. Прежде чем мы углубимся в это, давайте еще немного поговорим о том, для чего предназначен React, и почему я решил изучить его в этом посте.
Прежде всего, React не является полноценным веб-фреймворком. Это скорее инструментарий для разработки пользовательского интерфейса, а-ля GWT. Если вы хотите сделать HTTP-запрос для извлечения данных с сервера, React не предоставляет никаких утилит для этого. Тем не менее, он имеет огромную экосистему, которая предлагает множество библиотек и компонентов. Что я имею в виду под огромным? Скажем так: по данным npmjs.com , Angular имеет 17 938 пакетов . У React их почти в три раза больше — 42 428!
Angular мой хороший друг и был в течение долгого времени. Я не оставляю своего старого друга, чтобы принять React. Я просто заводил новых друзей. Для человека хорошо иметь много друзей с разным опытом и мнениями!
В этом посте показано, как вы можете создать пользовательский интерфейс и API как отдельные приложения. Вы узнаете, как создавать конечные точки REST с помощью Spring MVC, настраивать Spring Boot для разрешения CORS и создавать приложение React для отображения его данных. Это приложение покажет список пива из API, а затем принесет GIF из GIPHY, который соответствует имени пива. Я также покажу вам, как интегрировать Okta и ее поддержку OpenID Connect (OIDC), чтобы заблокировать ваш API и добавить аутентификацию в ваш пользовательский интерфейс.
Давайте начнем!
Создайте API с помощью Spring Boot
ПРИМЕЧАНИЕ. Приведенные ниже инструкции по созданию API Spring Boot такие же, как и в Bootiful Development с Spring Boot и Angular . Я скопировал их ниже для вашего удобства.
Чтобы начать работу с Spring Boot, перейдите к start.spring.io . В поле «Поиск зависимостей» выберите следующее:
- H2 : база данных в памяти
- JPA : стандартный ORM для Java
- Остальные репозитории : Позволяет вам представить ваши репозитории JPA как конечные точки REST
- Web : Spring MVC с Джексоном (для JSON), Hibernate Validator и встроенным Tomcat
Если вам больше нравится командная строка, вы можете использовать следующую команду для загрузки файла demo.zip с HTTPie .
http https: //start.spring.io/starter.zip \ dependencies==h2,data-jpa,data-rest,web -d |
Создайте каталог с именем spring-boot-react-example
, внутри которого находится каталог server
. Разверните содержимое demo.zip
в каталог server
.
Откройте проект «сервер» в вашей любимой IDE и запустите DemoApplication
или запустите его из командной строки, используя ./mvnw spring-boot:run
.
Создайте пакет Beer.java
файл Beer.java
в нем. Этот класс будет сущностью, которая содержит ваши данные.
package com.example.demo.beer; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Beer { @Id @GeneratedValue private Long id; private String name; public Beer() {} public Beer(String name) { this .name = name; } public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } @Override public String toString() { return "Beer{" + "id=" + id + ", name='" + name + '\ '' + '}' ; } } |
Добавьте класс BeerRepository
который использует Spring Data для выполнения CRUD на этом объекте.
package com.example.demo.beer; import org.springframework.data.jpa.repository.JpaRepository; interface BeerRepository extends JpaRepository<Beer, Long> { } |
Добавьте BeerCommandLineRunner
который использует этот репозиторий и создает набор данных по умолчанию.
package com.example.demo.beer; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import java.util.stream.Stream; @Component public class BeerCommandLineRunner implements CommandLineRunner { private final BeerRepository repository; public BeerCommandLineRunner(BeerRepository repository) { this .repository = repository; } @Override public void run(String... strings) throws Exception { // Top beers from https://www.beeradvocate.com/lists/top/ Stream.of( "Kentucky Brunch Brand Stout" , "Good Morning" , "Very Hazy" , "King Julius" , "Budweiser" , "Coors Light" , "PBR" ).forEach(name -> repository.save( new Beer(name)) ); repository.findAll().forEach(System.out::println); } } |
Перестройте свой проект, и вы увидите список пива, напечатанный в вашем терминале.
Добавьте аннотацию @RepositoryRestResource
в BeerRepository
чтобы представить все ее операции CRUD в качестве конечных точек REST.
import org.springframework.data.rest.core.annotation.RepositoryRestResource; @RepositoryRestResource interface BeerRepository extends JpaRepository<Beer, Long> { } |
Добавьте класс BeerController
чтобы создать конечную точку, которая отфильтровывает не очень BeerController
пиво.
package com.example.demo.beer; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @RestController public class BeerController { private BeerRepository repository; public BeerController(BeerRepository repository) { this .repository = repository; } @GetMapping ( "/good-beers" ) public Collection<Beer> goodBeers() { return repository.findAll().stream() .filter( this ::isGreat) .collect(Collectors.toList()); } private boolean isGreat(Beer beer) { return !beer.getName().equals( "Budweiser" ) && !beer.getName().equals( "Coors Light" ) && !beer.getName().equals( "PBR" ); } } |
Перестройте свое приложение и перейдите по http://localhost:8080/good-beers
. Вы должны увидеть список хорошего пива в вашем браузере.
Вы также должны увидеть этот же результат в окне терминала при использовании HTTPie.
http localhost:8080 /good-beers |
Создать проект с приложением Create React
Создание API кажется легкой частью в наши дни, во многом благодаря Spring Boot. В этом разделе я надеюсь показать вам, что создание пользовательского интерфейса с помощью React также довольно просто. Если вы выполните следующие шаги, вы создадите новое приложение React, получите названия пива и изображения из API и создадите компоненты для отображения данных.
Чтобы создать проект React, убедитесь, что у вас установлены Node.js , Create React App и Yarn .
|
В окне терминала перейдите в корень каталога spring-boot-react-example
и выполните следующую команду. Эта команда создаст новое приложение React с поддержкой TypeScript.
create-react-app client --scripts-version=react-scripts-ts |
После запуска этого процесса у вас будет новый каталог client
со всеми необходимыми зависимостями. Чтобы убедиться, что все работает, перейдите в каталог client
и запустите yarn start
. Если все работает, вы должны увидеть следующее в вашем браузере.
До сих пор вы создали API Good-Beers и приложение React, но не создали UI для отображения списка сортов пива из вашего API. Для этого откройте client/src/App.tsx
и добавьте метод componentDidMount()
.
componentDidMount() { this .setState({isLoading: true }); .then(response => response.json()) .then(data => this .setState({beers: data, isLoading: false })); } |
Жизненный цикл компонента React будет вызывать метод componentDidMount()
. Приведенный выше код использует fetch
, современную замену XMLHttpRequest
. Это поддерживается в большинстве браузеров в соответствии с caniuse.com .
Вы можете видеть, что он устанавливает состояние beers
с данными ответа. Чтобы инициализировать состояние для этого компонента, вам необходимо переопределить конструктор.
constructor(props: any) { super (props); this .state = { beers: [], isLoading: false }; } |
Чтобы это работало, вам нужно добавить типы параметров в сигнатуру класса. Код ниже показывает, как должна выглядеть верхняя часть вашего класса App
на данный момент.
class App extends React.Component<{}, any> { constructor(props: any) { super (props); this .state = { beers: [], isLoading: false }; } // componentDidMount() and render() } |
Измените метод render()
чтобы иметь следующий JSX. JSX — это XML-подобный синтаксис Facebook, который отображает HTML через JavaScript.
render() { const {beers, isLoading} = this.state; if (isLoading) { return < p >Loading...</ p >; } return ( < div className = "App" > < div className = "App-header" > < img src={logo} className = "App-logo" alt = "logo" /> < h2 >Welcome to React</ h2 > </ div > < div > < h2 >Beer List</ h2 > {beers.map((beer: any) => < div key={beer.id}> {beer.name} </ div > )} </ div > </ div > ); } |
Если вы посмотрите на http://localhost:3000
в вашем браузере, вы увидите сообщение «Загрузка…». Если вы загляните в консоль вашего браузера, вы, скорее всего, увидите проблему с CORS.
Failed to load http: //localhost :8080 /good-beers : No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. |
Чтобы решить эту проблему, вам нужно настроить Spring Boot, чтобы разрешить междоменный доступ с http://localhost:3000
.
Настройте CORS для Spring Boot
В проекте сервера откройте server/src/main/java/com/example/demo/beer/BeerController.java
и добавьте аннотацию @CrossOrigin
для включения совместного использования ресурсов между источниками (CORS) с клиента ( http://localhost:3000
).
import org.springframework.web.bind.annotation.CrossOrigin; ... @GetMapping ( "/good-beers" ) public Collection goodBeers() { |
После внесения этих изменений перезапустите сервер, обновите браузер, и вы сможете увидеть список пивных из вашего Spring Boot API.
Создать компонент BeerList
Чтобы упростить поддержку этого приложения, переместите App.tsx
и рендеринг списка пива из App.tsx
в его собственный компонент BeerList
. Создайте src/BeerList.tsx
и заполните его кодом из App.tsx
.
import * as React from 'react' ; class BeerList extends React.Component<{}, any> { constructor(props: any) { super (props); this .state = { beers: [], isLoading: false }; } componentDidMount() { this .setState({isLoading: true }); .then(response => response.json()) .then(data => this .setState({beers: data, isLoading: false })); } render() { const {beers, isLoading} = this .state; if (isLoading) { return <p>Loading...</p>; } return ( <div> <h2>Beer List</h2> {beers.map((beer: any) => <div key={beer.id}> {beer.name} </div> )} </div> ); } } export default BeerList; |
Затем измените client/src/App.tsx
чтобы он содержал только оболочку и ссылку на <BeerList/>
.
import * as React from 'react' ; import './App.css' ; import BeerList from './BeerList' ; const logo = require( './logo.svg' ); class App extends React.Component<{}, any> { render() { return ( <div className= "App" > <div className= "App-header" > <img src={logo} className= "App-logo" alt= "logo" /> <h2>Welcome to React</h2> </div> <BeerList/> </div> ); } } export default App; |
Создать компонент GiphyImage
Чтобы он выглядел немного лучше, добавьте компонент GIPHY для получения изображений на основе имени пива. Создайте client/src/GiphyImage.tsx
и поместите в него следующий код.
import * as React from 'react' ; interface GiphyImageProps { name: string; } class GiphyImage extends React.Component<GiphyImageProps, any> { constructor(props: GiphyImageProps) { super (props); this .state = { giphyUrl: '' , isLoading: false }; } componentDidMount() { const giphyApi = '//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=' ; fetch(giphyApi + this .props.name) .then(response => response.json()) .then(response => { if (response.data.length > 0 ) { this .setState({giphyUrl: response.data[ 0 ].images.original.url}); } else { // dancing cat for no images found this .setState({giphyUrl: '//media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif' }); } this .setState({isLoading: false }); }); } render() { const {giphyUrl, isLoading} = this .state; if (isLoading) { return <p>Loading image...</p>; } return ( <img src={giphyUrl} alt={ this .props.name} width= "200" /> ); } } export default GiphyImage; |
Измените метод render()
в BeerList.tsx
чтобы использовать этот компонент.
import GiphyImage from './GiphyImage' ; ... render() { const {beers, isLoading} = this .state; if (isLoading) { return <p>Loading...</p>; } return ( <div> <h2>Beer List</h2> {beers.map((beer: any) => <div key={beer.id}> {beer.name}<br/> <GiphyImage name={beer.name}/> </div> )} </div> ); } |
Результат должен выглядеть примерно как следующий список названий пива с изображениями.
Вы только что создали приложение React, которое взаимодействует с Spring Boot API с использованием междоменных запросов. Поздравляем!
Добавить поддержку PWA
Create React App имеет встроенную поддержку прогрессивных веб-приложений (PWA). Чтобы узнать, как он интегрирован, откройте client/README.md
и client/README.md
поиск «Создание прогрессивного веб-приложения».
Чтобы увидеть, как это работает, запустите yarn build
в client
каталоге. После завершения этой команды вы увидите сообщение, подобное следующему.
The build folder is ready to be deployed. You may serve it with a static server: yarn global add serve serve -s build |
Запустите предложенные команды, и вы сможете открыть браузер для просмотра http://localhost:5000
. Ваш браузер, скорее всего, покажет ошибку CORS на своей консоли, поэтому снова BeerController.java
и настройте его разрешенные источники, чтобы разрешить порт 5000.
|
Перезагрузите сервер, и http://localhost:5000
должен загрузить названия пива и изображения.
Я провел аудит Lighthouse в Chrome и обнаружил, что на данный момент это приложение набирает только 73/100 баллов.
Вы заметите на скриншоте выше, что «Манифест не имеет иконок по крайней мере 512 пикселей». Это звучит достаточно легко исправить. Вы можете скачать 512-пиксельный бесплатный значок пива с этой страницы .
ПРИМЕЧАНИЕ. Этот значок сделан Freepik с www.flaticon.com . Лицензировано CC 3.0 BY .
Скопируйте загруженный beer.png
в client/public
. Измените файл client/public/manifest.json
чтобы иметь имя, специфичное для этого приложения, и добавить 512-пиксельный значок.
{ "short_name" : "Beer" , "name" : "Good Beer" , "icons" : [ { "src" : "favicon.ico" , "sizes" : "192x192" , "type" : "image/png" }, { "src" : "beer.png" , "sizes" : "512x512" , "type" : "image/png" } ], "start_url" : "./index.html" , "display" : "standalone" , "theme_color" : "#000000" , "background_color" : "#ffffff" } |
После внесения этих изменений я смог набрать 82 очка Lighthouse для PWA. Самая заметная жалоба из этого отчета заключалась в том, что я не использовал HTTPS. Чтобы увидеть, как приложение получит оценку при использовании HTTPS, я развернул его в Pivotal Cloud Foundry и Heroku . Я был накачан, чтобы обнаружить, что он набрал ? на обеих платформах
Чтобы прочитать сценарии, которые я использовал для развертывания всего, см. cloudfoundry.sh
и heroku.sh
в репозитории GitHub этой статьи. Я благодарен @starbuxman и @codefinger за помощь в их создании!
Добавить аутентификацию с Okta
Вы можете подумать: «Это довольно круто, легко понять, почему люди влюбляются в React». Есть еще один инструмент, в который вы можете влюбиться после того, как попробуете его: аутентификация с Okta! Почему окта? Потому что вы можете получить 7000 активных ежемесячных пользователей бесплатно ! Это стоит попробовать, особенно если вы видите, как легко добавить аутентификацию в Spring Boot и взаимодействовать с Okta.
Okta Spring Boot Starter
Чтобы заблокировать сервер, вы можете использовать Okta’s Spring Boot Starter . Чтобы интегрировать этот стартер, добавьте следующую зависимость в server/pom.xml
:
< dependency > < groupId >com.okta.spring</ groupId > < artifactId >okta-spring-boot-starter</ artifactId > < version >0.2.0</ version > </ dependency > |
Вам также необходимо добавить раздел <dependencyManagement>
чтобы обновить поддержку OAuth в Spring Security.
< dependencyManagement > < dependencies > < dependency > < groupId >org.springframework.security.oauth</ groupId > < artifactId >spring-security-oauth2</ artifactId > < version >2.2.0.RELEASE</ version > </ dependency > </ dependencies > </ dependencyManagement > |
ПРИМЕЧАНИЕ. Существует проблема со стартером Spring Boot от Okta, когда он не работает с DevTools Spring Boot.
Теперь вам нужно настроить сервер на использование Okta для аутентификации. Для этого вам нужно будет создать приложение OIDC в Okta.
Создать приложение OIDC в Okta
Войдите в свою учетную запись Okta Developer (или зарегистрируйтесь, если у вас нет учетной записи) и выберите Приложения > Добавить приложение . Нажмите « Одностраничное приложение» , нажмите « Далее» и присвойте приложению имя, которое вы запомните. Измените все экземпляры localhost:8080
на localhost:3000
и нажмите Готово .
Скопируйте идентификатор клиента в файл server/src/main/resources/application.properties
. Пока вы там, добавьте свойство okta.oauth2.issuer
, соответствующее вашему домену Okta. Например:
okta.oauth2.issuer=https: // {yourOktaDomain}.com /oauth2/default okta.oauth2.clientId={clientId} |
ПРИМЕЧАНИЕ . Значение { yourOktaDomain }
должно быть примерно таким: dev-123456.oktapreview.com
. Убедитесь, что вы не включили -admin
в значение!
Обновите server/src/main/java/com/okta/developer/demo/DemoApplication.java
чтобы включить его в качестве сервера ресурсов.
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; @EnableResourceServer @SpringBootApplication |
После внесения этих изменений вы сможете перезагрузить сервер и увидеть, что доступ запрещен при попытке перейти по адресу http: // localhost: 8080 .
Реактивная поддержка Okta
React SDK от Okta позволяет интегрировать OIDC в приложение React. Вы можете узнать больше о Okta React SDK на сайте npmjs.com . Для установки выполните следующие команды:
yarn add @okta /okta-react react-router-dom yarn add -D @types /react-router-dom |
React SDK от Okta зависит от реакции-роутера , поэтому причина для установки react-router-dom
. Настройка маршрутизации в client/src/App.tsx
является обычной практикой, поэтому замените ее код на TypeScript ниже, который устанавливает аутентификацию с Okta.
import * as React from 'react' ; import './App.css' ; import Home from './Home' ; import { BrowserRouter as Router, Route } from 'react-router-dom' ; import { Security, ImplicitCallback } from '@okta/okta-react' ; const config = { redirectUri: window.location.origin + '/implicit/callback' , clientId: '{clientId}' }; export interface Auth { login(): {}; logout(): {}; isAuthenticated(): boolean ; getAccessToken(): string; } class App extends React.Component { render() { return ( <Router> <Security issuer={config.issuer} client_id={config.clientId} redirect_uri={config.redirectUri} > <Route path= "/" exact={ true } component={Home}/> <Route path= "/implicit/callback" component={ImplicitCallback}/> </Security> </Router> ); } } export default App; |
Создайте client/src/Home.tsx
чтобы он содержал оболочку приложения, App.tsx
ранее содержала App.tsx
. Этот класс отображает оболочку приложения, а также кнопки входа / выхода из системы и <BeerList/>
если вы аутентифицированы.
import * as React from 'react' ; import './App.css' ; import BeerList from './BeerList' ; import { withAuth } from '@okta/okta-react' ; import { Auth } from './App' ; const logo = require( './logo.svg' ); interface HomeProps { auth: Auth; } interface HomeState { authenticated: boolean ; } export default withAuth( class Home extends React.Component<HomeProps, HomeState> { constructor(props: HomeProps) { super (props); this .state = {authenticated: false }; this .checkAuthentication = this .checkAuthentication.bind( this ); this .checkAuthentication(); } async checkAuthentication() { const isAuthenticated = await this .props.auth.isAuthenticated(); const {authenticated} = this .state; if (isAuthenticated !== authenticated) { this .setState({authenticated: isAuthenticated}); } } componentDidUpdate() { this .checkAuthentication(); } render() { const {authenticated} = this .state; let body = null ; if (authenticated) { body = ( <div className= "Buttons" > <button onClick={ this .props.auth.logout}>Logout</button> <BeerList auth={ this .props.auth}/> </div> ); } else { body = ( <div className= "Buttons" > <button onClick={ this .props.auth.login}>Login</button> </div> ); } return ( <div className= "App" > <div className= "App-header" > <img src={logo} className= "App-logo" alt= "logo" /> <h2>Welcome to React</h2> </div> {body} </div> ); } }); |
Если вы посмотрите на свое приложение React в браузере, вы, вероятно, увидите ошибку, подобную следующей:
. /src/Home .tsx (4,26): error TS7016: Could not find a declaration file for module '@okta/okta-react' . '/Users/mraible/spring-boot-react-example/client/node_modules/@okta/okta-react/dist/index.js' implicitly has an 'any' type . Try `npm install @types/@okta /okta-react ` if it exists or add a new declaration (.d.ts) file containing ` declare module '@okta/okta-react' ;` |
Создайте client/src/okta.d.ts
со следующей декларацией для решения этой проблемы.
declare module '@okta/okta-react' ; |
Перезапустите клиент, и вы увидите, что над компонентом BeerList
нужно проделать определенную BeerList
.
. /src/Home .tsx (44,21): error TS2339: Property 'auth' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<BeerList> & Readonly<{ children?: ReactNode; }> & ...'. |
В client/src/BeerList.tsx
добавьте свойство auth
в реквизиты, создав интерфейс BeerListProps
который передается в сигнатуру класса.
import { Auth } from './App' ; interface BeerListProps { auth: Auth; } interface BeerListState { beers: Array<{}>; isLoading: boolean ; } class BeerList extends React.Component<BeerListProps, BeerListState> { ... } |
Добавьте следующие правила CSS в client/src/App.css
чтобы сделать кнопки входа / выхода из системы немного более заметными.
.Buttons { margin- top : 10px; } .Buttons button { font-size: 1em; } |
Ваш браузер shoa, проверьте следующее повторно.
Когда вы нажимаете кнопку, чтобы войти, введите адрес электронной почты и пароль, которые вы использовали для создания учетной записи Okta Developer. Когда он перенаправляет вас обратно в ваше приложение, вы, скорее всего, увидите «Загрузка…» и ошибку CORS в консоли вашего браузера.
Эта ошибка возникает из-за того, что @CrossOrigin
не @CrossOrigin
Spring Security. Чтобы решить эту проблему, добавьте simpleCorsFilter
компонент simpleCorsFilter
в тело DemoApplication.java
.
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; import java.util.Collections; @EnableResourceServer @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication. class , args); } @Bean public FilterRegistrationBean simpleCorsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials( true ); config.setAllowedMethods(Collections.singletonList( "*" )); config.setAllowedHeaders(Collections.singletonList( "*" )); source.registerCorsConfiguration( "/**" , config); FilterRegistrationBean bean = new FilterRegistrationBean( new CorsFilter(source)); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; } } |
Чтобы все это работало на клиенте, измените метод componentDidMount()
в client/src/BeerList.tsx
чтобы установить заголовок авторизации.
async componentDidMount() { this .setState({isLoading: true }); try { headers: { Authorization: 'Bearer ' + await this .props.auth.getAccessToken() } }); const data = await response.json(); this .setState({beers: data, isLoading: false }); } catch (err) { this .setState({error: err}); } } |
Вам также необходимо добавить error
в интерфейсе BeerListState
.
interface BeerListState { beers: Array<{}>; isLoading: boolean ; error: string; } |
Измените конструктор, чтобы он инициализировал error
пустой строкой.
this .state = { beers: [], isLoading: false , error: '' }; |
Затем измените метод render()
чтобы при возникновении ошибки отображалась ошибка.
render() { const {beers, isLoading, error} = this .state; if (isLoading) { return <p>Loading ...</p>; } if (error.length > 0 ) { return <p>Error: {error}</p>; } return (...) } |
Теперь вы сможете увидеть список пива как аутентифицированный пользователь.
Если это работает, поздравляю!
Очистить эти предупреждения TypeScript
Вы можете заметить, что консоль вашего браузера выдает некоторые предупреждения TypeScript.
. /src/BeerList .tsx [16, 22]: Type declaration of 'any' loses type -safety. Consider replacing it with a more precise type , the empty type ( '{}' ), or suppress this occurrence. [52, 27]: Type declaration of 'any' loses type -safety. Consider replacing it with a more precise type , the empty type ( '{}' ), or suppress this occurrence. . /src/GiphyImage .tsx [7, 59]: Type declaration of 'any' loses type -safety. Consider replacing it with a more precise type , the empty type ( '{}' ), or suppress this occurrence. |
Чтобы исправить первую проблему, измените client/src/BeerList.tsx
чтобы его конструктор client/src/BeerList.tsx
следующим образом:
constructor(props: BeerListProps) { ... } |
Для второго вопроса создайте интерфейс Beer
в client/src/BeerList.tsx
. Поместите это рядом с другими интерфейсами наверху.
interface Beer { id: number; name: string; } |
Затем измените { beers.map((beer: any) =>
на { beers.map((beer: Beer) =>
.
Третья проблема может быть решена путем создания нового интерфейса GiphyImageState
в client/src/GiphyImage.tsx
для определения свойств состояния.
interface GiphyImageState { giphyUrl: string; isLoading: boolean ; } class GiphyImage extends React.Component<GiphyImageProps, GiphyImageState> { ... } |
После внесения этих изменений вы должны избавиться от предупреждений TypeScript.
Узнайте больше о Spring Boot и React
Чтобы узнать больше о React, Spring Boot или Okta, ознакомьтесь со следующими ресурсами:
- Мастер-класс по вступлению в React Эрика Висенти — очень рекомендуется для изучения React!
- Разговор Мой Угол против Реакции Smackdown в Devoxx Бельгия с Deepu K Sasidharan
- Как получить данные в React от Робина Виеруха
- Создайте приложение React с аутентификацией пользователя за 15 минут
- Создайте приложение Preact с аутентификацией
- Создайте пользовательскую форму входа с помощью Okta’s React SDK
Вы можете найти исходный код, связанный с этой статьей, на GitHub . Основной пример (без аутентификации) находится в master
ветви, а интеграция Okta — в ветви okta
. Чтобы проверить ветку Okta на вашем локальном компьютере, выполните следующие команды.
git checkout okta |
Если вы обнаружите какие-либо проблемы, пожалуйста, добавьте комментарий ниже, и я сделаю все возможное, чтобы помочь. Если вам понравился этот урок, я бы хотел, чтобы вы следили за мной в Twitter . Чтобы получать уведомления о других статьях, подобных этой, следите за @oktadev .
«Я люблю писать код аутентификации и авторизации». Нет Java-разработчика. Надоело строить одни и те же экраны входа снова и снова? Попробуйте API Okta для размещенной аутентификации, авторизации и многофакторной аутентификации.