Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialSean Thomas
1,681 PointsAPI that serves Thymeleaf pages (for a web portal) and JSON responses for iOS and Android clients
I need a web backend that serves a web portal (primarily for admin purposes) as well as the iOS and Android versions of our mobile app. I have no intention of exposing the API beyond these components -- i.e., "our clients". What's the best way to go about this? Note that both the website and mobile clients will be CRUD'ing the same resources. Does it make sense to go with a Controller implementation (as in the giflib-hibernate and todotoday projects)? I'm thinking of having base controllers that do business logic but then either return a Thymleaf URI or serialized JSON (for the mobile clients). Is this even possible?
Thanks in advance!
4 Answers
Craig Dennis
Treehouse TeacherI think you should watch the Build a REST API in Spring workshop. Just what you are looking for.
Sean Thomas
1,681 PointsCraig, I watched and enjoyed the workshop. In addition to the building an API like what you made, I also want to serve Thymeleaf URI's for a web portal. That requires exposing the Controllers, I think? So, in short, I'm wondering how I serve JSON to mobile clients but also Thymeleaf URI's to a web portal.
Craig Dennis
Treehouse TeacherTake a look at the Spring-A-Gram example project. There are a couple of talks from Greg Turnquist showing this off that will probably help solidify things. Hope to get to these Spring microservices soon here!
Juan Felipe Calle
Courses Plus Student 21,013 PointsHi @SeanThomas, how did you manage your project ? I'm trying to implement a similar project and i want to hear about your experience.
Thanks
Binyamin Friedman
14,615 PointsYou can have your api on the path /api/v1 like Craig did and then you can simply add controllers like you would on a regular project. Or do you want something different?
Łukasz Kusowski
13,422 PointsYou need to make regular controllers as in any Spring course, but you have to alter the WebSecurity class with the following one. Just remember
package com.cieplastronaswiata.core;
import com.cieplastronaswiata.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@EnableWebSecurity
public class MultiHttpSecurityConfiguration {
@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// TODO: REMOVE HTTP BASICS AND ENABLE CSRF FOR REAL-LIFE APPLICATION.
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
@Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(loginSuccessHandler())
.failureHandler(loginFailureHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/login")
.and()
.csrf();
}
public AuthenticationSuccessHandler loginSuccessHandler() {
return (request, response, autherntication) -> response.sendRedirect("/");
}
public AuthenticationFailureHandler loginFailureHandler() {
return (request, response, autherntication) -> {
request.getSession().setAttribute("flash", new FlashMessage("Incorrect username and/or password. Please try again.", FlashMessage.Status.FAILURE));
response.sendRedirect("/login");
};
}
@Bean
public EvaluationContextExtension securityExtension() {
return new EvaluationContextExtensionSupport() {
@Override
public String getExtensionId() {
return "security";
}
@Override
public Object getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
};
}
}
}
Sean Thomas
1,681 PointsSean Thomas
1,681 PointsOr maybe a hybrid approach would be better? In other words, one projects that uses Controllers for the web portal but the abstracted controller logic in this course for the mobile API? Then (necessarily) using different endpoints for either. Thoughts?