I am facing a problem using a REST service made up with Spring Boot 1.5. I am developing a REST service that acts as a proxy, forwarding requests to another REST service that exposes the same API.
#RestController
#RequestMapping("/user")
public class ProxyUserController {
// Some initialization code
#PostMapping
public ResponseEntity<?> add(#Valid #RequestBody User user) {
return restTemplate.postForEntity(userUrl, user, String.class);
}
#Configuration
public static class RestConfiguration {
#Bean
public RestTemplate restTemplate(UserErrorHandler errorHandler) {
return new RestTemplateBuilder().errorHandler(errorHandler).build();
}
}
#Component
public static class UserErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) {
return false;
}
#Override
public void handleError(ClientHttpResponse response) {
// Empty body
}
}
}
As you can see, to avoid that RestTemplate surrounds any error response with an exception that causes the creation of a new response with status 500, I defined a customer ResponseErrorHandler.
The problem I faced is that if the postForEntity returns a response with an HTTP Status different from 200, the response will never arrive at the caller, that hangs up until the timeout hits him.
However, if I create a new response starting from the one returned by the postForEntity, all starts to work smoothly.
#PostMapping
public ResponseEntity<?> add(#Valid #RequestBody User user) {
final ResponseEntity<?> response =
restTemplate.postForEntity(userUrl, user, String.class);
return ResponseEntity.status(response.getStatusCode()).body(response.getBody());
}
What the hell is going on? Why I can't reuse a ResponseEntity coming from another call?
Thanks to all.
Related
My application consists of multiple services.We had a requirement now that for every request coming in to our application we need to validate the token.
Current architecture of my application is such that every microservice has its own ServiceInterceptor class and in that class I am writing the logic in prehandle method to validate token recieved in request.
Service Interceptor Class.
#Component
public class ServiceInterceptor implements HandlerInterceptor {
private static final ApplicationLogger logger = ApplicationLogger.getInstance();
#Autowired
TokenInfoServiceImpl tokenInfoServiceImpl;
#Override
#CrossOrigin(origins = "*", maxAge = 3600)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestPath = request.getRequestURL().toString();
String authToken = request.getHeader("authToken");
String bearerToken = request.getHeader("Authorization");
String userId = request.getHeader("userId");
if (deviceId.equals("web")) {
if (bearerToken.startsWith("Bearer ")){
bearerToken = bearerToken.substring(7, bearerToken.length());
} else {
response.sendError(400, "Expected bearer prefix to Authorization header value.");
}
boolean isTokenValid = tokenInfoServiceImpl.validateToken(bearerToken);
return isTokenValid;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
My concern is since we have different services,every service has its interceptor class , so in every service will I have to create method validateToken(to validate the token when a request comes to that service) which is obviously not at all an ideal approach.
Is there a way that I could write validateToken method in one place and that could be accessed by all the services(like UserService,PaymentService,etc..) or rather one Interceptor could be used to intercept request for all the individual microservices instead of having separate interceptor for each service .
I know this can be done using API Gateway but right now our team want a quick solution to this .API Gateway will implement later.
If I understand your question & comments you can try below :
Create Configuration bean which implements WebMvcConfigurer
Use your ServiceInterceptor inside addInteceptor & mention endpoints or root context if all endpoints needed this config :
#Configuration
public class ConfigClass implements WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ServiceInterceptor ()).addPathPatterns("/contextroot/**");
}
}
Also you may directly use your ServiceInterceptor without annotating it with Component.
#Service
#GetMapping
public Foo findByFooId(#RequestParam(name = "fid") String fooId) {
return fooService.findByFooId(fooId);
}
I would like to trigger and save who viewed Foo, using a different method in FooService.
Its like a PostConstruct callback for a successful response of findByFooId. How can this be achieved
One way is going to a custom HandlerInterceptor implementation.
Definition of the interceptor
public class FooViewerInterceptor extends HandlerInterceptorAdapter {
#Autowired
FooService fooService;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
// if response succeeded ? http response code = 200 ?
// extract the "who" logic
// extract the fooId from request path
fooService.viewedBy(fooId, userId); // example...
}
}
Register the interceptor. Note the path pattern specified with the custom interceptor instance.. just an example.
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FooViewerInterceptor()).addPathPatterns("/foo/**");
}
}
I have an reactive spring boot application where I am having different controllers and all the controller having get, post, put, delete methods
GET and DELETE method URI format => /{userName}/{others}
and it's ensured that put and post method must have a field userid in their request body.
Also All the request having an authorization header.
And I already have a method called validate that accepts 2 parameters authorizationHeader and userName and returns true if this mapping exists false if not.
I am trying to write generic filter can filter incoming request and validate before going to controller.
How can I write this generic webfilter especially how to extract body from post request and validate requests.
I tried writing this
#Component
#Slf4j
public class ExampleWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
ObjectMapper mapper = new ObjectMapper();
return serverWebExchange
.getRequest()
.getBody()
.next()
.flatMap(body -> {
try {
return validate(body, serverWebExchange
.geHeaders().get(0))
} catch (IOException e) {
return Mono.error(e);
}
})
.flatMap((boolean s) -> {
return webFilterChain.filter(serverWebExchange);
});
}
Mono<Boolean> validate(DataBuffer body, String Header){
//my logic to validate
}
}
But it seems it's hanging after this filter method executed. so my question is
How can I write webfilter which will read body and validate?
Is there any other generic solution available for this type of problem in spring-boot?
I think you should use Interceptors. You can intercept the http call, and make your validations on the request. You can do this as global or you can do this for specific endpoints/paths. Here is a example for your case.
#Component
public class ProductServiceInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
return true;
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//make validations
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {
//make validations
}
}
After this you need to register your interceptor like below.
#Component
public class ProductServiceInterceptorAppConfig extends WebMvcConfigurerAdapter {
#Autowired
ProductServiceInterceptor productServiceInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(productServiceInterceptor);
}
}
For more depth information you can visit the links below.
https://www.youtube.com/watch?v=agBadIAx0Wc
https://www.tutorialspoint.com/spring_boot/spring_boot_interceptor.htm
Let's say I have the following controller. (Assume that Order.customer is the customer the order belongs to and only they should be able to access it.)
#RestController
#RequestMapping("/orders")
public class OrderController {
#GetMapping
#PostAuthorize("returnObject.customer == authentication.principal")
public Order getOrderById(long id) {
/* Look up the order and return it */
}
}
After looking up the order, #PostAuthorize is used to make sure it belongs to the authenticated customer. If it is not, Spring responds with a 403 Forbidden.
Such an implementation has a problem: Clients can distinguish between orders that do not exist and orders they have no access to. Ideally, 404 should be returned in both cases.
While this could be solved by injecting the Authentication into the handler method and implementing custom logic there, is there any way to achieve this using #PostAuthorize or a similar, declarative API?
You can specify a custom AccessDeniedHandler in your Spring Security configuration.
In the following example, the handler will return a 404 Not Found on an access denied failure.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler(accessDeniedHandler())
);
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
}
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.sendError(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.getReasonPhrase());
}
}
You could try a ControllerAdvice to catch and transform the AccessDeniedException, which PostAuthorize throws.
#RestControllerAdvice
public class ExceptionHandlerController {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(AccessDeniedException.class)
public String handleAccessDenied(AccessDeniedException e) {
return "nothing here"; // or a proper object
}
}
I have a jersey web service. I want to write test cases for it.
My service is
#Path(value = "/mock")
#Component
public class MockService
{
private static Log log = LogFactory.getLog(MockService.class);
#POST
#Path(value = "/{mockrequest:ABCD}")
#Produces(MediaType.JSON)
public Response mockOKResponse(#Context ContainerRequestContext request, #PathParam("mockrequest") EnumMockService mockService)
{
return Response.ok().build();
}
#GET
#Path(value = "/{mockrequest}")
#Produces("application/pdf")
public Response mockProducesPDF(#Context ContainerRequestContext request, #PathParam("mockrequest") EnumMockService mockService)
{
MockAccountTable testccount = jdbcTestAcountDao.getTestAccountResponseBlob(mockService.toString());
byte[] responseBlob = testccount != null ? testccount.getResponseBlob() : null;
return Response.ok(responseBlob).build();
}
}
I am planing to write test case for this.Which calls specific method based on the specific reuest.
#RunWith(MockitoJUnitRunner.class)
public class TestMockService
extends TestCaseSrs
{
private MockService mockService = new MockService();
#Override
#Before
public void prepare()
throws Exception
{
MockitoAnnotations.initMocks(this);
}
#Test
public void testGetIncreasedHardwares()
{
//dynamically call method based on request
}
}
I am not sure how to set request type here to call method accordingly rather than calling method directly.
It would be great if some one can help me with the approach.