Rest easy response status + body - httpresponse

I have following method in my rest service:
#POST
#Path("/create")
#ResponseStatus(HttpStatus.CREATED)
#Consumes(MediaType.WILDCARD)
public String create( .... ) {.... return json;}
so I want to get a response with json in body and status code CREATED.
The problem is: I can't get a response the CREATED status.
The status code is allways OK, so it seems that "#ResponseStatus(HttpStatus.CREATED)" is just ignored...
Can somebody help me with it?
I'm using hibernate 4.1, spring 3.1 and resteasy 2.3

As far as I know, it's not possible to achieve this by annotating the method with #org.springframework.web.bind.annotation.ResponseStatus.
You can return javax.ws.rs.core.Response from your method:
return Response
.status(Response.Status.CREATED)
.entity("ok")
.build();
Or you can have org.jboss.resteasy.spi.HttpResponse injected, and set the status code directly.
There might be more ways of doing this, but I'm only aware of these two.
Working testcase:
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.interception.PostProcessInterceptor;
import org.junit.Assert;
import org.junit.Test;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
public class ResponseTest {
#Path("/")
public static class Service {
#Context HttpResponse response;
#GET
#Path("/1")
public Response createdUsingResponse() throws NotFoundException {
return Response
.status(Response.Status.CREATED)
.entity("ok")
.build();
}
#GET
#Path("/2")
public String created() throws NotFoundException {
response.setStatus(Response.Status.CREATED.getStatusCode());
return "ok";
}
}
public static class Interceptor implements PostProcessInterceptor {
#Context HttpResponse response;
#Override
public void postProcess(ServerResponse response) {
if(this.response.getStatus() != 0){
response.setStatus(this.response.getStatus());
}
}
}
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(new Service());
dispatcher
.getProviderFactory()
.getServerPostProcessInterceptorRegistry()
.register(new Interceptor());
{
MockHttpRequest request = MockHttpRequest.get("/1");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals(201, response.getStatus());
}
{
MockHttpRequest request = MockHttpRequest.get("/2");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals(201, response.getStatus());
}
}
}

Related

How to validate a model model in web flux SpringBoot

I'm trying to move from traditional approach to Reactive style. Early days for me. One of the challenge I came into and could not make much progress is on model validation. With RestControllers, it was as easy as #Valid.
I don't see anything out there to make it happen for Webflux way of doing things
package com.reactive.sbhello.handler;
import com.reactive.sbhello.model.Order;
import com.reactive.sbhello.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import javax.validation.Validator;
#Component
public class OrderHandler {
#Autowired
private OrderService orderService;
private final Validator validator;
public OrderHandler(Validator validator) {
this.validator = validator;
}
public Mono<ServerResponse> getAll(ServerRequest request) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(orderService.getAllOrders(),
Order.class
);
}
public Mono<ServerResponse> getOrderInfo(ServerRequest request) {
var orderId = request.pathVariable("orderId");
var response = orderService.getOrderById(Integer.parseInt(orderId));
return response.collectList()
.flatMap(orders -> {
if(orders.isEmpty()) {
return ServerResponse.badRequest().body(BodyInserters.fromValue("Invalid OrderId"));
} else {
return ServerResponse.ok().body(BodyInserters.fromValue(orders));
}
});
}
public Mono<ServerResponse> addOrder(ServerRequest request) {
return request.bodyToMono(Order.class)
.flatMap(order -> orderService.addOrder(order))
.flatMap(order -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(order)));
}
}
"addOrder" function at the moment lacks any validation. As a result, null values go through. Is there anyway to validate apart from doing it in service and bubble up the error? Or should I stick to RestController approach and still use streaming from there.

Spring Boot RestController DELETE request fails without .csrf().disable()

I have a Spring Boot Rest Service proof of concept.
I have this for my security: (obviously a poor real implmentation).
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
/* not production grade quality */
httpSecurity.authorizeRequests().anyRequest().permitAll();
}
// #Override
// public void configure(WebSecurity web) throws Exception {
// web.debug(true);
// }
}
Using Postman:
All of my GETs were working fine. Then I added a DELETE request. and got
{
"timestamp": "blah blah blah",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/v1/mything/1"
}
Postman setup: (not rocket science)
DELETE
http://localhost:8080/v1/mythings/1
So I added the ".csrf().disable()", and my DELETE works.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
/* not production grade quality */
httpSecurity.csrf().disable(); /* had to add this "Cross Site Request Forgery" disable for DELETE operations */
httpSecurity.authorizeRequests().anyRequest().permitAll();
}
// #Override
// public void configure(WebSecurity web) throws Exception {
// web.debug(true);
// }
}
But my question is WHY does .csrf().disable() .. allow DELETE requests? Seems somewhat unrelated.
Thanks.
My full rest controller below:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.inject.Inject;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
#RestController
#RequestMapping("/v1")
public class MyThingController {
private final Logger logger;
private final IMyThingManager mythingManager;
/* The Inject annotation is the signal for which constructor to use for IoC when there are multiple constructors. Not needed in single constructor scenarios */
#Inject
public MyThingController(IMyThingManager mythingManager) {
this(LoggerFactory.getLogger(MyThingController.class), mythingManager);
}
public MyThingController(Logger lgr, IMyThingManager mythingManager) {
if (null == lgr) {
throw new IllegalArgumentException("Logger is null");
}
if (null == mythingManager) {
throw new IllegalArgumentException("IMyThingManager is null");
}
this.logger = lgr;
this.mythingManager = mythingManager;
}
#RequestMapping(value = "/mythings", method = RequestMethod.GET)
Collection<MyThingDto> getAllMyThings() {
Collection<MyThingDto> returnItems = this.mythingManager.getAll();
return returnItems;
}
#RequestMapping(method = RequestMethod.GET, value = "mythings/{mythingKey}")
ResponseEntity<MyThingDto> getMyThingById(#PathVariable Long mythingKey) {
this.logger.info(String.format("Method getMyThingById called. (mythingKey=\"%1s\")", mythingKey));
Optional<MyThingDto> foundItem = this.mythingManager.getSingle(mythingKey);
ResponseEntity<MyThingDto> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
if (foundItem.isPresent()) {
responseEntity = new ResponseEntity<>(foundItem.get(), HttpStatus.OK);
}
return responseEntity;
}
#RequestMapping(value = "mythings/{mythingKey}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Integer> deleteUser(#PathVariable("mythingKey") Long mythingKey) {
this.logger.info(String.format("Method deleteUser called. (mythingKey=\"%1s\")", mythingKey));
int rowCount = this.mythingManager.deleteByKey(mythingKey);
int rowCount = 1; /* use this to "fake it" */
ResponseEntity<Integer> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
if (rowCount > 0) {
responseEntity = new ResponseEntity<>(rowCount, HttpStatus.OK);
}
return responseEntity;
}
}
CSRF protection checks for a CSRF token on changing methods like POST, PUT, DELETE. And as a REST API is stateless you don't have a token in a cookie. That's why you have to disable it for REST APIs.
References
https://security.stackexchange.com/questions/166724/should-i-use-csrf-protection-on-rest-api-endpoints
Spring Security Reference: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#csrf
Guide to CSRF Protection in Spring https://www.baeldung.com/spring-security-csrf
The guide to CSRF Protection says: "However, if our stateless API uses a session cookie authentication, we need to enable CSRF protection as we'll see next."
For this case the solution is not to disable csrf. Is there another possibility to use CSRF Protection with DELETE

How to prevent Redis writes for anonymous user sessions

I have this sample application:
package com.example.session;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class DemoRedisDataSessionApplication {
#Configuration
#EnableWebSecurity
#EnableRedisHttpSession(redisNamespace = "demo-redis-data-session")
public static class AppConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("0000").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and()
.authorizeRequests().antMatchers("/ping").permitAll().and()
.authorizeRequests().anyRequest().fullyAuthenticated();
}
}
#RestController
public static class AppController {
#GetMapping("/ping")
public String ping() {
return "pong";
}
#GetMapping("/secured")
public String secured() {
return "secured";
}
}
public static void main(String[] args) {
SpringApplication.run(DemoRedisDataSessionApplication.class, args);
}
}
When I hit /secured I get 302 redirected to the /login form, which is what I expect if I am not logged in, but I get some unwanted entries in Redis:
127.0.0.1:6379> keys *
1) "spring:session:demo-redis-data-session:sessions:expires:dbb124b9-c37d-454c-8d67-409f28cb88a6"
2) "spring:session:demo-redis-data-session:expirations:1515426060000"
3) "spring:session:demo-redis-data-session:sessions:dbb124b9-c37d-454c-8d67-409f28cb88a6"
I don't want to create this data for every anonymous user (read crawler), so is there a way to prevent these Redis entries when hitting a secured endpoint/page with an anonymous user?
Additional data used for this sample project
 docker-compose.yml
version: "2"
services:
redis:
image: redis
ports:
- "6379:6379"
Spring Boot version
1.5.9.RELEASE
This is not the optimal solution since it creates only one session for all crawlers, but at least I don't get Redis full of unwanted session.
import lombok.extern.log4j.Log4j;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.MultiHttpSessionStrategy;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#Log4j
#Component
public class CrawlerManagerSessionStrategyWrapper implements MultiHttpSessionStrategy {
private CookieHttpSessionStrategy delegate;
private volatile String crawlerSessionId;
public CrawlerManagerSessionStrategyWrapper() {
this.delegate = new CookieHttpSessionStrategy();
}
public String getRequestedSessionId(HttpServletRequest request) {
String sessionId = getSessionIdForCrawler(request);
if (sessionId != null)
return sessionId;
else {
return delegate.getRequestedSessionId(request);
}
}
public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
delegate.onNewSession(session, request, response);
if (isCrawler(request)) {
crawlerSessionId = session.getId();
}
}
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
delegate.onInvalidateSession(request, response);
}
public HttpServletRequest wrapRequest(HttpServletRequest request, HttpServletResponse response) {
return request;
}
public HttpServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
return response;
}
private String getSessionIdForCrawler(HttpServletRequest request) {
if (isCrawler(request)) {
SessionRepository<Session> repo = (SessionRepository<Session>) request.getAttribute(SessionRepository.class.getName());
if (crawlerSessionId != null && repo != null) {
Session session = repo.getSession(crawlerSessionId);
if (session != null) {
return crawlerSessionId;
}
}
}
return null;
}
private boolean isCrawler(HttpServletRequest request) {
// Here goes the logic to understand if the request comes from a crawler, for example by checking the user agent.
return true;
}
}
The only thing to implement is the isCrawler method to state if the request comes from a crawler.

How to call other eureka client in a Zuul server

application.properties
zuul.routes.commonservice.path=/root/path/commonservice/**
zuul.routes.commonservice.service-id=commonservice
zuul.routes.customer.path=/root/path/customer/**
zuul.routes.customer.service-id=customer
zuul.routes.student.path=/root/path/student/**
zuul.routes.student.service-id=student
and below is my custom filter
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.openreach.gateway.common.constant.CommonConstant;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
#Component
public class HeaderFilter extends ZuulFilter {
private static final Logger log = LoggerFactory.getLogger(HeaderFilter.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpSession httpSession = context.getRequest().getSession();
String idOrEmail = context.getRequest().getHeader("coustom");
if (httpSession.getAttribute("someAttributes") == null) {
if (idOrEmail != null) {
//call the common-service and get details and set it first
//then call the customer service with common-service details
} else {
//call the customer service
}
} else {
log.info("data excits");
// routrs the request to the backend with the excisting data details
}
context.addZuulResponseHeader("Cookie", "JSESSIONID=" + httpSession.getId());
return null;
}
}
I'm using the ribbon load balancer with zuul. My problem is that how should I call the common-service first? I need all my requests to check the header value and then call the actual service end point.
First, use the #LoadBalanced qualifier to create your RestTemplate bean which is load balanced.
#LoadBalanced
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
And Inject the bean into the filter
#Autowired
RestTemplate restTemplate;
Then you can get result by restTemplate's method like below
String result = restTemplate.postForObject("http://commonservice/url", object, String.class);
ref: http://cloud.spring.io/spring-cloud-static/spring-cloud.html#_spring_resttemplate_as_a_load_balancer_client

Spring-Boot - Error Handling

I'm trying to write error handler in Spring-Boot for my controllers that would catch most possible errors (Spring, sql etc.). So far I'm able to get JSON response with Nulls however i'm unable to put any data inside. When I try to get error message in I just receive a blank page.
import java.io.IOException;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
#RestController
public class BasicErrorController implements ErrorController {
private static final String ERROR_PATH = "/error";
#RequestMapping(value=ERROR_PATH)
#ExceptionHandler(value = {NoSuchRequestHandlingMethodException.class, SQLException.class, IOException.class, RuntimeException.class, Exception.class})
public ErrorBody defaultErrorHandler(HttpServletRequest request, Exception e) {
ErrorBody eBody = new ErrorBody();
eBody.setMessage(e.getCause().getMessage());
return eBody;
}
}
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
public class ErrorBody {
private String dateTime;
private String exception;
private String url;
private String message;
}
Yo can do something like this:
#ControllerAdvice
public class ControllerExceptionTranslator {
#ExceptionHandler(EntityNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
SimpleErrorMessage handleException(EntityNotFoundException exception){
log.debug("Entity Not Found Exception {}",exception.getMessage());
log.trace(exception.getMessage(),exception);
return new SimpleErrorMessage("Entity not found","This resource was not found");
}
#ExceptionHandler({UsernameNotFoundException.class})
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#ResponseBody
SimpleErrorMessage handleException(UsernameNotFoundException exception){
log.debug("Username not found {}",exception.getLocalizedMessage());
log.trace(exception.getMessage(),exception);
return new SimpleErrorMessage("Unaouthorized"," ");
}
}
I was able to get to data about errors and send them as json properly by using "HttpServletRequest request" and reading information from request.
#RequestMapping(value = ERROR_PATH)
public ErrorBody defaultErrorHandler(HttpServletRequest request) {....}
Here this is an example of #ExceptionHandler(Exception.class)
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
You can use #ControllerAdvice
package demo.controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
#ControllerAdvice
public class ExceptionControllerAdvice {
#InitBinder
public void initBinder(WebDataBinder binder) {
System.out.println("controller advice: init binder");
}
#ExceptionHandler(Exception.class)
public String exception(Exception e) {
System.out.println("controller advice: exception Handler");
System.out.println(e.getMessage());
return "error";
}
#ModelAttribute
public void modelAttribute(){
System.out.println("controller advice:model Attribute");
}
}

Resources