Erste Verbesserungsrunde für die Reddit-Anwendung

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();
}

Weiter - Lassen Sie uns sehen, wie Sie überprüfen, ob ein Link bereits zuvor an Reddit gesendet wurde.

Hier sind unseresubmissionForm.html:




Check if already submitted



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.