Erste Runde der Verbesserungen an der Reddit-Anwendung
1. Überblick
The Reddit web application Case Study bewegt sich gut - und die kleine Webanwendung entwickelt sich und wird langsam nutzbar.
In dieser Ausgabe werden wir die vorhandenen Funktionen geringfügig verbessern - einige nach außen gerichtet, andere nicht - und im Allgemeinenmaking the app better.
2. Einrichtungsprüfungen
Beginnen wir mit einigen einfachen, aber nützlichen Überprüfungen, die ausgeführt werden müssen, wenn die Anwendung gebootet wird:
@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();
}
Beachten Sie, wie wir hier die Annotation@PostConstructverwenden, um uns nach Abschluss des Abhängigkeitsinjektionsprozesses in den Lebenszyklus der Anwendung einzubinden.
Die einfachen Ziele sind:
-
Überprüfen Sie, ob alle Eigenschaften vorhanden sind, die für den Zugriff auf die Reddit-API erforderlich sind
-
Überprüfen Sie, ob die Persistenzschicht funktioniert (indem Sie einen einfachenfindAll-Aufruf ausgeben).
Wenn wir versagen, tun wir das früh.
3. Das Reddit-Problem "Zu viele Anfragen"
Die Reddit-API ist aggressiv bei Ratenbegrenzungsanforderungen, die keine eindeutigen "User-Agent" senden.
Also - wir müssen diesen eindeutigenUser-Agent-Header zu unserenredditRestTemplate hinzufügen - unter Verwendung eines benutzerdefiniertenInterceptor:
3.1. Erstellen Sie benutzerdefinierteInterceptor
Hier ist unser benutzerdefinierter Interceptor -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. Konfigurieren SieredditRestTemplate
Wir müssen diesen Interceptor natürlich mit denredditRestTemplate einrichten, die wir verwenden:
@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. Konfigurieren Sie die H2-Datenbank zum Testen
Als Nächstes richten wir eine In-Memory-Datenbank - H2 - zum Testen ein. Wir müssen diese Abhängigkeit zu unserenpom.xml hinzufügen:
com.h2database
h2
1.4.187
Und definieren Sie einpersistence-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. Wechseln Sie zu Thymeleaf
JSP ist raus und Thymeleaf ist rein.
5.1. Ändern Siepom.xml
Zuerst müssen wir diese Abhängigkeiten zu unserer pom.xml hinzufügen:
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. Erstellen SieThymeleafConfig
Weiter - ein einfachesThymeleafConfig:
@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;
}
}
Und addiere es zu unserenServletInitializer:
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(PersistenceJPAConfig.class, WebConfig.class,
SecurityConfig.class, ThymeleafConfig.class);
return context;
}
5.3. Ändern Siehome.html
Und eine schnelle Änderung der Homepage:
Schedule to Reddit
6. Ausloggen
Lassen Sie uns nun einige Verbesserungen vornehmen, die für den Endbenutzer der Anwendung tatsächlich sichtbar sind. Wir beginnen mit der Abmeldung.
Wir fügen der Anwendung eine einfache Abmeldeoption hinzu, indem wir unsere Sicherheitskonfiguration ändern:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.....
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
7. Subreddit Autocomplete
Als nächstes implementieren wira simple autocomplete functionality, um das Subreddit zu füllen. Manuelles Schreiben ist kein guter Weg, da es eine faire Chance gibt, etwas falsch zu machen.
Beginnen wir mit der Client-Seite:
Einfach genug. Nun die Serverseite:
@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. Überprüfen Sie, ob der Link bereits auf Reddit vorhanden ist
Weiter - Lassen Sie uns sehen, wie Sie überprüfen, ob ein Link bereits zuvor an Reddit gesendet wurde.
Hier sind unseresubmissionForm.html:
Und hier ist unsere Controller-Methode:
@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. Bereitstellung in Heroku
Schließlich - wir werden die Bereitstellung für Heroku einrichten - und deren kostenlose Stufe verwenden, um die Beispiel-App zu betreiben.
9.1. Ändern Siepom.xml
Zuerst müssen wir dieseWeb Runner plugin zu denpom.xml hinzufügen:
org.apache.maven.plugins
maven-dependency-plugin
2.3
package
copy
com.github.jsimone
webapp-runner
7.0.57.2
webapp-runner.jar
Hinweis - Wir werden Web Runner verwenden, um unsere App auf Heroku zu starten.
Wir werden Postgresql auf Heroku verwenden - daher müssen wir vom Treiber abhängig sein:
org.postgresql
postgresql
9.4-1201-jdbc41
9.2. DieProcfile
Wir müssen den Prozess, der auf dem Server ausgeführt wird, inProcfile wie folgt definieren:
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war
9.3. Heroku App erstellen
Um eine Heroku-App aus Ihrem Projekt zu erstellen, gehen wir einfach wie folgt vor:
cd path_to_your_project
heroku login
heroku create
9.4. Datenbankkonfiguration
Als Nächstes müssen wir unsere Datenbank mithilfe der DatenbankeigenschaftenPostgresunserer App konfigurieren.
Hier ist zum Beispiel 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
Beachten Sie, dass wir die Datenbankdetails [Hostname, Datenbankname, Benutzer und Kennwort] aus dem Heroku-Dashborad abrufen müssen.
Wie in den meisten Fällen ist das Schlüsselwort "Benutzer" einreserved word in Postgres, daher müssen wir den Namen unserer Entitätstabelle "User" ändern:
@Entity
@Table(name = "APP_USER")
public class User { .... }
9.5. Code an Heoku senden
Lassen Sie uns jetzt den Code an Heroku senden:
git add .
git commit -m "init"
git push heroku master
10. Fazit
In diesem vierten Teil unserer Fallstudie standen kleine, aber wichtige Verbesserungen im Vordergrund. Wenn Sie mitgemacht haben, können Sie sehen, wie sich dies zu einer interessanten und nützlichen kleinen App entwickelt.