I am practicing spring boot OAuth2. I have and authorization server, resource server, and client server. I managed to secure an endpoint (/products).
I want show a list of products using Thymeleaf.
Here is the controller in the resource server:
#RestController
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping("/products")
public List<Product> getProducts(final Model model) {
return (List<Product>) productRepository.findAll();
}
}
Here is the controller in the client server:
#RestController
public class ArticlesController {
#Autowired
private WebClient webClient;
#GetMapping(value = "/articles")
public String[] getArticles(
#RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient) {
return this.webClient.get().uri("http://localhost:8090/articles")
.attributes(oauth2AuthorizedClient(authorizedClient)).retrieve().bodyToMono(String[].class).block();
}
#GetMapping(value = "/products")
public List<Product> getProducts(
#RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient) {
return this.webClient.get().uri("http://localhost:8090/products")
.attributes(oauth2AuthorizedClient(authorizedClient)).accept(MediaType.ALL).retrieve()
.bodyToFlux(Product.class).collectList().block();
}
}
With the code above I have a list of JSON Products. What is the best way to show this list as a table with Thymeleaf using the webClient?
Related
I am missing something here. I am attempting to pull information using Spring Boot WebClient from a Dummy Api that's an Http request. I am not getting any info pulled when I go into postman.
Thanks for any insight you can give me. I am still very new to coding and self-taught.
Here's my employee controller:
#Autowired
WebClientApp webClientApp;
#GetMapping("/consume")
public String getEmployee(Model model) {
model.addAttribute("listEmployees", empServiceImpl.getAllEmployees());
model.addAttribute("listemps", webClientApp.webClientBuilder());
return "index";
}
Web Client
private WebClient webClient;
public void SimpleWebClient(WebClient webClient) {
this.webClient = webClient;
}
public Flux<Employee> webClientBuilder() {
return this.webClient
//this.webClientBuilder = webClientBuilder.baseUrl(DummyEmployee)
.get()
.uri("api/v1/employees")
.retrieve()
.bodyToFlux(Employee.class);
}
Employee
#Data
#ToString
//#AllArgsConstructor
//#NoArgsConstructor
#JsonRootName(value = "data")
public class Employee {
#JsonProperty("id")
public int employeeID;
#JsonProperty("employee_name")
public String employeeName;
#JsonProperty("employee_salary")
public String employeeSalary;
#JsonProperty("employee_age")
public int employeeAge;
#JsonProperty("employee_image")
public Blob employeeImage;
}
Service
#Repository
#ComponentScan(basePackages = {"com.example.app.repository"})
#Service
public class ServiceImpl implements EmpService{
#Autowired
private EmployeeRepository employeeRepo;
#SuppressWarnings("unchecked")
public List<Employee> getAllEmployees() {
return (List<Employee>) employeeRepo.findAll();
}
}
Service
#Service
public interface EmpService {
static List<Employee> getAllEmployees() {
// TODO Auto-generated method stub
return null;
}
}
Main
public static void main(String[] args) {
SpringApplication.run(RestWebsiteDataProjectApplication.class, args);
}
#Bean
public WebClient webClientFromScratch() {
return WebClient.builder()
.baseUrl("https://dummy.restapiexample.com/")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
Flux only emits its content when it is subscribed. You are not subscribing to the Flux returned by the webClientBuilder() method.
You shouldn't really do this, but try adding .block() to your Controller as follows:
#Autowired
WebClientApp webClientApp;
#GetMapping("/consume")
public String getEmployee(Model model) {
model.addAttribute("listEmployees", empServiceImpl.getAllEmployees());
model.addAttribute("listemps", webClientApp.webClientBuilder().block());
return "index";
}
If this works, please consider reworking your code because while working with Spring WebFlux (reactive programming) you should always deal with Mono and Flux so that you can take full advantage of the reactive stack.
I have created a small application using the spring boot framework. I have created a Rest Controler class.
and deploy it on tomcat, but I am getting 404 error i.e
The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
#RestController
#RequestMapping("students")
public class StudentController {
#Autowired
StudentRepository repository;
#GetMapping
public List<Student> getAllStudents() {
return (List<Student>) repository.findAll();
}
#PostMapping
public String createStudent() {
return "created";
}
#PutMapping
public String updateStudent() {
return "updated";
}
#DeleteMapping
public String deleteStudent() {
return "deleted";
}
}
You are missing slash in annotation, it should look like this
#RestController
#RequestMapping("/students")
public class StudentController {
...
}
I have implemented custom GatewayFilterFactory filter. But I don't know how to test this filter with e2e setup.
I have referenced official spring-cloud-gateway AddRequestHeaderGatewayFilterFactoryTests test case code.
This is my custom filter code:
#Component
public class MyCustomFilter implements GatewayFilterFactory<MyCustomFilter.Config>, Ordered {
#Override
public GatewayFilter apply(Config config) {
return new OrderedGatewayFilter((this::filter), getOrder());
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/* do some filtering */
}
#Override
public int getOrder() {
return 1000;
}
#Override
public Config newConfig() {
return new Config(MyCustomFilter.class.getSimpleName());
}
public static getConfig() {
return
}
#Getter
#Setter
public static class Config {
private String name;
Config(String name) {
this.name = name;
}
}
}
And this is my test code:
BaseWebClientTests class look exactly the same as official BaseWebClientTests class code
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
#DirtiesContext
#ActiveProfiles("my-custom-filter")
public class MyCustomFilterTests extends BaseWebClientTests {
#LocalServerPort
protected int port = 0;
protected WebTestClient testClient;
protected WebClient webClient;
protected String baseUri;
#Before
public void setup() throws Exception {
setup(new ReactorClientHttpConnector(), "http://localhost:" + port);
}
protected void setup(ClientHttpConnector httpConnector, String baseUri) {
this.baseUri = baseUri;
this.webClient = WebClient.builder().clientConnector(httpConnector)
.baseUrl(this.baseUri).build();
this.testClient = WebTestClient
.bindToServer(httpConnector)
.baseUrl(this.baseUri)
.build();
}
#Test
public void shouldFailByFilterTests() {
/* This test should be failed but success :( */
testClient.get().uri("/api/path")
.exchange().expectBody(Map.class).consumeWith(result -> {
/* do assertion */
});
}
#EnableAutoConfiguration
#SpringBootConfiguration
#Import(DefaultTestConfig.class)
public static class TestConfig {
#Value("${test.uri}")
String uri;
#Bean
public MyCustomFilter myCustomFilter() {
return new MyCustomFilter();
}
#Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder, MyCustomFilter myCustomFilter) {
return builder.routes().route("my_custom_filter",
r -> r.path("/api/path")
.filters(f -> f.filter(myCustomFilter.apply(new MyCustomFilter.Config("STRING"))))
.uri(uri))
.build();
}
}
}
Lastly Target controller looks like this:
#RestController
#RequestMapping("/api/path")
public class HttpBinCompatibleController {
#GetMapping("/")
public Mono<BodyData> identity() {
return Mono.just(new BodyData("api success"));
}
#NoArgsConstructor
#AllArgsConstructor
#Getter
static class BodyData {
private String message;
}
}
What I understand how this filter factory test code works is that
custom filter: custom filter is setup inside TestConfig class testRouteLocator method
target controller: target controller is defined as HttpBinCompatibleController class
testClient sends the request, and custom should do some filtering, then target controller should receive the request from testClient.
What I expect from this shouldFailByFilterTests TC is that before request from testClient is sent to target controller, that request should be rejected by MyCustomFilter. But the request is sent to the target controller.
I think the request from testClient is not proxied by testRouteLocator but I'm not sure
Question
What is the cause of this problem?
Is there another way to test my own custom filter?
This problem was related to the version incompatibility between Spring Boot and Spring Cloud.
I was using Spring Boot version 2.1.7 and Spring Cloud version Greenwich.SR2.
Then I found this 'Release train Spring Boot compatibility' table on this link
Before I've noticed version incompatibility, for using #Configuration(proxyBeanMethods = false) feature, upgraded Spring Boot version to 2.2.x.
The solution is using 2.1.x branch BaseWebClientTests class.
In my microservice I have Spring boot controller which is receive also auth token from http request header and send it to another microservice for validation:
#Autowired
private AuthService authService;
#GetMapping(path = "/find", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
#Timed
#CrossOrigin
public ResponseEntity<?> find(
#RequestParam(name = "contactId", required = true) final Long contactId,
#RequestHeader(name = "secret-token", required = true) String token
) {
boolean checkTokenValidationFromAuthServer = authService.checkTokenValidationFromAuthServer(token);
if (!checkTokenValidationFromAuthServer) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("token is invalid");
}
return ResponseEntity.status(HttpStatus.OK).body("Your contact here!");
}
Auth Service must be like this
#Service
#Component
public class AuthService {
public boolean checkTokenValidationFromAuthServer(String token){
//make rest call to auth microservice
//validate token, return false, or true
return false;
}
}
Basic test always will fail because I from test I do not know secret token, I want to mock or prevent controller from validation:
#RunWith(SpringRunner.class)
#SpringBootTest
public class PoolApplicationTests {
#Autowired
private PoolController controller;
#Test
public void controllerInitializedCorrectly() {
assertThat(controller).isNotNull();
}
#Test
public void testCall() {
ResponseEntity<?> find = controller.find(1L, "token");
assertThat(find.getStatusCode()).isEqualTo(200);
}
}
I do not know what is the best strategy for this kind of cases.
I have spring controllers in my app, when executing my app the all controllers end URLs are showing not fond(404) error in browser.
public class EmployeeContoller {
private EmployeeService employeeService; #Autowired.
public EmployeeContoller (EmployeeService employeeService)
{this.employeeService=employeeService. #RequestMapping("/employee/{id}")
public Employee getEmployee(#PathVariable String id){ return employeeService.getEmployee(I'd);
}