Première série d'améliorations à l'application Reddit
1. Vue d'ensemble
The Reddit web application Case Study avance bien - et la petite application Web prend forme et devient peu à peu utilisable.
Dans cet épisode, nous allons apporter de petites améliorations aux fonctionnalités existantes - certaines extérieures, d'autres non - et généralementmaking the app better.
2. Vérifications de la configuration
Commençons par quelques vérifications simples mais utiles qui doivent être exécutées lorsque l'application est amorcée:
@Autowired
private UserRepository repo;
@PostConstruct
public void startupCheck() {
if (StringUtils.isBlank(accessTokenUri) ||
StringUtils.isBlank(userAuthorizationUri) ||
StringUtils.isBlank(clientID) || StringUtils.isBlank(clientSecret)) {
throw new RuntimeException("Incomplete reddit properties");
}
repo.findAll();
}
Notez comment nous utilisons ici l'annotation@PostConstruct pour nous connecter au cycle de vie de l'application, une fois le processus d'injection de dépendances terminé.
Les objectifs simples sont:
-
vérifier si nous avons toutes les propriétés nécessaires pour accéder à l'API Reddit
-
vérifier que la couche de persistance fonctionne (en émettant un simple appelfindAll)
Si nous échouons - nous le faisons tôt.
3. Le problème Reddit «Trop de requêtes»
L'API Reddit est agressive dans les requêtes de limitation de débit qui n'envoient pas un "User-Agent" unique.
Donc - nous devons ajouter cet en-tête uniqueUser-Agent à nosredditRestTemplate - en utilisant unInterceptor personnalisé:
3.1. Créer desInterceptor personnalisés
Voici notre intercepteur personnalisé -UserAgentInterceptor:
public class UserAgentInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("User-Agent", "Schedule with Reddit");
return execution.execute(request, body);
}
}
3.2. ConfigurerredditRestTemplate
Nous devons bien sûr configurer cet intercepteur avec lesredditRestTemplate que nous utilisons:
@Bean
public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
List list = new ArrayList();
list.add(new UserAgentInterceptor());
template.setInterceptors(list);
return template;
}
4. Configurer la base de données H2 pour les tests
Ensuite - allons-y et configurez une base de données en mémoire - H2 - pour les tests. Nous devons ajouter cette dépendance à nospom.xml:
com.h2database
h2
1.4.187
Et définissez unpersistence-test.properties:
## DataSource Configuration ###
jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:oauth_reddit;DB_CLOSE_DELAY=-1
jdbc.user=sa
jdbc.pass=
## Hibernate Configuration ##
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=update
5. Passer à Thymeleaf
JSP est sorti et Thymeleaf est présent.
5.1. Modifierpom.xml
Premièrement, nous devons ajouter ces dépendances à notre pom.xml:
org.thymeleaf
thymeleaf
2.1.4.RELEASE
org.thymeleaf
thymeleaf-spring4
2.1.4.RELEASE
org.thymeleaf.extras
thymeleaf-extras-springsecurity3
2.1.2.RELEASE
5.2. CréerThymeleafConfig
Suivant - un simpleThymeleafConfig:
@Configuration
public class ThymeleafConfig {
@Bean
public TemplateResolver templateResolver() {
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/jsp/");
templateResolver.setSuffix(".jsp");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new SpringSecurityDialect());
return templateEngine;
}
@Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
}
Et ajoutez-le à nosServletInitializer:
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(PersistenceJPAConfig.class, WebConfig.class,
SecurityConfig.class, ThymeleafConfig.class);
return context;
}
5.3. Modifierhome.html
Et une modification rapide de la page d'accueil:
Schedule to Reddit
6. Se déconnecter
Maintenant, faisons quelques améliorations qui sont réellement visibles pour l'utilisateur final de l'application. Nous allons commencer par la déconnexion.
Nous ajoutons une option de déconnexion simple dans l'application en modifiant notre configuration de sécurité:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.....
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
7. Sous-reddit Autocomplete
Ensuite - implémentonsa simple autocomplete functionality pour le remplir dans le sous-répertoire; l'écrire manuellement n'est pas une bonne façon de procéder, car il y a de bonnes chances de se tromper.
Commençons par le côté client:
Assez simple. Maintenant, le côté serveur:
@RequestMapping(value = "/subredditAutoComplete")
@ResponseBody
public String subredditAutoComplete(@RequestParam("term") String term) {
MultiValueMap param = new LinkedMultiValueMap();
param.add("query", term);
JsonNode node = redditRestTemplate.postForObject(
"https://oauth.reddit.com//api/search_reddit_names", param, JsonNode.class);
return node.get("names").toString();
}
8. Vérifiez si le lien est déjà sur Reddit
Ensuite, voyons comment vérifier si un lien est déjà soumis avant Reddit.
Voici nossubmissionForm.html:
Et voici notre méthode de contrôleur:
@RequestMapping(value = "/checkIfAlreadySubmitted", method = RequestMethod.POST)
@ResponseBody
public String checkIfAlreadySubmitted(
@RequestParam("url") String url, @RequestParam("sr") String sr) {
JsonNode node = redditRestTemplate.getForObject(
"https://oauth.reddit.com/r/" + sr + "/search?q=url:" + url + "&restrict_sr=on", JsonNode.class);
return node.get("data").get("children").toString();
}
9. Déploiement sur Heroku
Enfin, nous allons configurer le déploiement sur Heroku et utiliser leur offre gratuite pour alimenter l'exemple d'application.
9.1. Modifierpom.xml
Tout d'abord, nous devrons ajouter ceWeb Runner plugin auxpom.xml:
org.apache.maven.plugins
maven-dependency-plugin
2.3
package
copy
com.github.jsimone
webapp-runner
7.0.57.2
webapp-runner.jar
Remarque - nous utiliserons Web Runner pour lancer notre application sur Heroku.
Nous allons utiliser Postgresql sur Heroku - nous devrons donc avoir une dépendance au pilote:
org.postgresql
postgresql
9.4-1201-jdbc41
9.2. LesProcfile
Nous devons définir le processus qui s'exécutera sur le serveur dans unProcfile - comme suit:
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war
9.3. Créer une application Heroku
Pour créer une application Heroku à partir de votre projet, nous allons simplement:
cd path_to_your_project
heroku login
heroku create
9.4. Configuration de la base de données
Ensuite, nous devons configurer notre base de données en utilisant les propriétés de base de donnéesPostgresde notre application.
Par exemple, voici persistence-prod.properties:
## DataSource Configuration ##
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://hostname:5432/databasename
jdbc.user=xxxxxxxxxxxxxx
jdbc.pass=xxxxxxxxxxxxxxxxx
## Hibernate Configuration ##
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.hbm2ddl.auto=update
Notez que nous devons obtenir les détails de la base de données [nom d’hôte, nom de la base de données, utilisateur et mot de passe] dans le tableau de bord Heroku.
De plus, comme dans la plupart des cas, le mot-clé «utilisateur» est unreserved word in Postgres, nous devons donc changer le nom de notre table d'entités «User»:
@Entity
@Table(name = "APP_USER")
public class User { .... }
9.5. Pousser le code vers Heoku
Maintenant, transmettons le code à Heroku:
git add .
git commit -m "init"
git push heroku master
10. Conclusion
Dans cette quatrième partie de notre étude de cas, l'accent était mis sur des améliorations minimes mais importantes. Si vous avez suivi, vous pouvez voir comment cela s'annonce pour devenir une petite application intéressante et utile.