Primeira Rodada de Melhorias no Aplicativo Reddit
1. Visão geral
The Reddit web application Case Study está se movendo muito bem - e o pequeno aplicativo da web está se moldando e lentamente se tornando utilizável.
Nesta edição, faremos pequenas melhorias na funcionalidade existente - algumas voltadas para o exterior, outras não - e geralmentemaking the app better.
2. Verificações de configuração
Vamos começar com algumas verificações simples - mas úteis - que precisam ser executadas quando o aplicativo é inicializado:
@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();
}
Observe como estamos usando a anotação@PostConstruct aqui para entrar no ciclo de vida do aplicativo, depois que o processo de injeção de dependência terminar.
Os objetivos simples são:
-
verifique se temos todas as propriedades necessárias para acessar a API do Reddit
-
verifique se a camada de persistência está funcionando (emitindo uma chamada simplesfindAll)
Se falharmos - fazemos isso cedo.
3. O problema do Reddit “Muitos pedidos”
A API Reddit é agressiva em solicitações de limitação de taxa que não enviam um “User-Agent“ exclusivo
Portanto, precisamos adicionar este cabeçalhoUser-Agent exclusivo ao nossoredditRestTemplate - usando umInterceptor personalizado:
3.1. CriarInterceptor personalizado
Aqui está nosso interceptor personalizado -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. ConfigureredditRestTemplate
É claro que precisamos configurar este interceptor com osredditRestTemplate que estamos usando:
@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. Configurar banco de dados H2 para teste
Em seguida, vamos configurar um banco de dados na memória - H2 - para teste. Precisamos adicionar esta dependência ao nossopom.xml:
com.h2database
h2
1.4.187
E defina umpersistence-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. Mudar para Thymeleaf
JSP está fora e Thymeleaf está dentro
5.1. Modificarpom.xml
Primeiro, precisamos adicionar essas dependências ao nosso 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. CrieThymeleafConfig
Próximo - um simplesThymeleafConfig:
@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;
}
}
E adicione-o ao nossoServletInitializer:
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(PersistenceJPAConfig.class, WebConfig.class,
SecurityConfig.class, ThymeleafConfig.class);
return context;
}
5.3. Modificarhome.html
E uma rápida modificação da página inicial:
Schedule to Reddit
6. Sair
Agora - vamos fazer algumas melhorias que são realmente visíveis para o usuário final do aplicativo. Vamos começar com o logout.
Estamos adicionando uma opção de logout simples ao aplicativo, modificando nossa configuração de segurança:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.....
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
7. Subreddit Autocomplete
Em seguida - vamos implementara simple autocomplete functionality para preencher o subreddit; escrevê-lo manualmente não é uma boa opção, já que há uma boa chance de errar.
Vamos começar com o lado do cliente:
Simples o suficiente. Agora, do lado do servidor:
@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. Verifique se o link já está no Reddit
A seguir - vamos ver como verificar se um link já foi enviado antes para o Reddit.
Aqui está nossosubmissionForm.html:
E aqui está o nosso método de controle:
@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. Implantação no Heroku
Por fim - vamos configurar a implantação no Heroku - e usar seu nível gratuito para alimentar o aplicativo de amostra.
9.1. Modificarpom.xml
Primeiro, precisaremos adicionar esteWeb Runner plugin aopom.xml:
org.apache.maven.plugins
maven-dependency-plugin
2.3
package
copy
com.github.jsimone
webapp-runner
7.0.57.2
webapp-runner.jar
Nota - usaremos o Web Runner para iniciar nosso aplicativo no Heroku.
Vamos usar o Postgresql no Heroku - então precisamos ter uma dependência para o driver:
org.postgresql
postgresql
9.4-1201-jdbc41
9.2. OProcfile
Precisamos definir o processo que será executado no servidor em umProcfile - da seguinte forma:
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war
9.3. Criar aplicativo Heroku
Para criar um aplicativo Heroku a partir do seu projeto, vamos simplesmente:
cd path_to_your_project
heroku login
heroku create
9.4. Configuração do banco de dados
Em seguida, precisamos configurar nosso banco de dados usando as propriedades de banco de dadosPostgres do nosso aplicativo.
Por exemplo, aqui está 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
Observe que precisamos obter os detalhes do banco de dados [nome do host, nome do banco de dados, usuário e senha] do painel do Heroku.
Além disso, como na maioria dos casos, a palavra-chave “usuário” é umreserved word in Postgres, então precisamos alterar o nome da nossa tabela de entidades “User”:
@Entity
@Table(name = "APP_USER")
public class User { .... }
9.5. Envie o código para Heoku
Agora - vamos enviar o código ao Heroku:
git add .
git commit -m "init"
git push heroku master
10. Conclusão
Nesta quarta parte do nosso Estudo de Caso, o foco foram pequenas, mas importantes melhorias. Se você tem acompanhado, pode ver como isso está se transformando em um pequeno aplicativo interessante e útil.