How do I get the response status code of a void method? - feign

/I'm trying to use a Feign-client to communicate another rest service
which will return status code 204 with no body/
public interface DepartmentApi {
#RequestLine("GET /department/nocontent") /*Department Client*/
#Headers("Content-Type: application/json")
ResponseEntity<Void> getDepartment();
}
#Component
public class ClientApiFactory {
#Bean
#RequestScope
public DepartmentApi getDepartmentApi() { /*Bean for Department client */
return HystrixFeign.builder()
.logLevel(Logger.Level.BASIC)
.decoder(new JacksonDecoder())
.encoder(new JacksonEncoder())
.target(DepartmentApi.class, "http://localhost:8080");
}
}
#GetMapping(value = "/nocontent") /*Department Service which is running on 8080*/
ResponseEntity<Void> noContent() {
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
I would like to retrieve the status code from the response for the void methods, but with a void method there is no way to get to the status ,it's returns[ReponseEntity] null.
Is there a way to retrieve the HTTP status code from a Feign method for a resource that returns no body? They all fail with a nullpointer exception because of the lack of response body.

Related

Authentication.Challenge not working with ApiController

With ApiController, Authentication.Challenge not prompting Microsoft login for SSO. it executes SignIn action method, with out any errors. If I change from ApiController to Controller then it's prompting. does any one know how to prompt for Microsoft login using ApiController?
public class ValuesController : ApiController
{
[System.Web.Http.Route("api/values/signin")]
[System.Web.Http.HttpGet]
public void SignIn()
{
if (!System.Web.HttpContext.Current.Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
public class ValuesController : Controller
{
public void SignIn()
{
if (!System.Web.HttpContext.Current.Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
We also faced a similar problem on our product.
The issue was the following: Challenge sets 401 status code for current response, which is later handled by a responsible OWIN Middleware, so if status code is not 401 the middleware won't handle the response and won't trigger the redirect.
But the default behavior of void action of ApiController sets 204 response status code. Therefore 401 is overwritten with 204, as a result nothing happens.
So there are several solutions:
Don't use ApiController if you can
Use ApiController but not void action. Use for example something like this
public ActionResult SignIn()
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(...);
return new HttpStatusCodeResult(HttpContext.GetOwinContext().Response.StatusCode);
}
If you have to use a void method and ApiController then you can end the response and then the status code won't be modified.
public void SignIn()
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(...);
System.Web.HttpContext.Current.Response.End();
}

keycloak throw authentication error from custom protocol mapper

I've crated a java custom protocol mapper extended from AbstractOIDCProtocolMapper
This mapper call a rest api, I want to show a custom message error on login based on te result of response. But I do not know how to do it
I am overriden the method
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession)
You can throw an Exception that extends org.keycloak.services.ErrorResponseException. There you can override
#Override
public Response getResponse() {
if (response != null) {
return response;
} else {
OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
return Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE).build();
}
}
passing any object as entity to be returned as part of the response.

Micronaut: Test POST request

In my Micronaut app I have a simple REST controller:
public class Response {
private String code;
public Response(String code) {
this.code = code;
}
}
#Controller("/api/test")
public class TestController {
#Post("/")
public Response index() {
return new Response("OK");
}
}
How can I tests this edpoint? I tried using
#MicronautTest
public class TestControllerTest {
#Inject
EmbeddedServer server;
#Inject
#Client("/")
HttpClient client;
#Test
void testResponse() {
String response = client.toBlocking()
.retrieve(HttpRequest.POST("/api/test/")); // FIXME `HttpRequest.POST` requires body
assertEquals("{\"code\": \"OK\"}", response);
}
but HttpRequest.POST requires an additional body argument to be specified. In my case there is no body to be sent. (In the real code it is a request to initialize a new object and thus it has to be POST).
Usually, when you implement a POST action, you expect that there is a body sent with the request. In your example, you don't accept any POST body, but you still need to pass anything in the unit test.
You can instantiate the HttpRequest object in the following way:
HttpRequest.POST("/api/test/", "");
You can't pass null, it has to be some non-null value (like an empty string.)

How to forward jwt token in Vert.x REST service

I have a Vert.x REST service that receive requests with jwt tokens, and I want to call my another REST service passing received token. Between router handler and WebClient call I have a business logic layer. My question is if there is a method to provide token to webClient other than passing it explicitly through my business logic layer? In other words is it possible to retrieve somehow my RoutingContext and token from e.g. vertxContext or an other component?
Example code demonstrating what I would like to achieve:
Verticle cass
public class RestApiVerticle extends AbstractVerticle {
businessLogicService service;
#Override
public void start() throws Exception {
initService();
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
JWTAuth authProvider = JWTAuth.create(vertx, getAuthConfig());
router.route("/*").handler(JWTAuthHandler.create(authProvider));
router.route("/somePath").handler(this::handleRequest);
server.requestHandler(router::accept).listen(config().getInteger("port"));
}
private void handleRequest(RoutingContext context){
service.doSomeBusinessLogic(); //I could pass context here, but I thing this is not a proper way to do it, as business logic should not know about RequestContext
}
private void initService(){
ExternalAPICaller caller = new ExternalAPICaller(WebClient.create(vertx));
service = new BusinessLogicService(caller);
}
private JsonObject getAuthConfig() {
return new JsonObject();
}
}
BusinessLogicService:
public class BusinessLogicService {
ExternalAPICaller caller;
public BusinessLogicService(ExternalAPICaller caller){
this.caller = caller;
}
public void doSomeBusinessLogic(){
caller.doSth();
}
}
ExternalAPICaller:
public class ExternalAPICaller {
WebClient client;
public ExternalAPICaller(WebClient client){
this.client = client;
}
public void doSth(){
String TOKEN = null; // I would like to retrive here my token from some vertx component
client.post("externalAPIpath")
.putHeader("Authorization", "Bearer" + TOKEN)
.send(ctx -> {
//(..)
});
}
}
My implementation is in JavaScript (Node.js/Express), but I used cookies to send the JWT to the client.
res.cookie("auth", token);
return res.redirect(`http://localhost:3000/socialauthredirect`);
When you call your do business logic method you could pass the request authorization header value as it contains your untouched jwt token. Then on your web client add a header with that value and of course named authorization and your token is forwarded to the next service.

How to make the #RestController do not response data as restful? [duplicate]

I have a REST endpoint implemented with Spring MVC #RestController. Sometime, depends on input parameters in my controller I need to send http redirect on client.
Is it possible with Spring MVC #RestController and if so, could you please show an example ?
Add an HttpServletResponse parameter to your Handler Method then call response.sendRedirect("some-url");
Something like:
#RestController
public class FooController {
#RequestMapping("/foo")
void handleFoo(HttpServletResponse response) throws IOException {
response.sendRedirect("some-url");
}
}
To avoid any direct dependency on HttpServletRequest or HttpServletResponse I suggest a "pure Spring" implementation returning a ResponseEntity like this:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(newUrl));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);
If your method always returns a redirect, use ResponseEntity<Void>, otherwise whatever is returned normally as generic type.
Came across this question and was surprised that no-one mentioned RedirectView. I have just tested it, and you can solve this in a clean 100% spring way with:
#RestController
public class FooController {
#RequestMapping("/foo")
public RedirectView handleFoo() {
return new RedirectView("some-url");
}
}
redirect means http code 302, which means Found in springMVC.
Here is an util method, which could be placed in some kind of BaseController:
protected ResponseEntity found(HttpServletResponse response, String url) throws IOException { // 302, found, redirect,
response.sendRedirect(url);
return null;
}
But sometimes might want to return http code 301 instead, which means moved permanently.
In that case, here is the util method:
protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
As the redirections are usually needed in a not-straightforward path, I think throwing an exception and handling it later is my favourite solution.
Using a ControllerAdvice
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {
NotLoggedInException.class
})
protected ResponseEntity<Object> handleNotLoggedIn(
final NotLoggedInException ex, final WebRequest request
) {
final String bodyOfResponse = ex.getMessage();
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", ex.getRedirectUri());
return handleExceptionInternal(
ex, bodyOfResponse,
headers, HttpStatus.FOUND, request
);
}
}
The exception class in my case:
#Getter
public class NotLoggedInException extends RuntimeException {
private static final long serialVersionUID = -4900004519786666447L;
String redirectUri;
public NotLoggedInException(final String message, final String uri) {
super(message);
redirectUri = uri;
}
}
And I trigger it like this:
if (null == remoteUser)
throw new NotLoggedInException("please log in", LOGIN_URL);
if you #RestController returns an String you can use something like this
return "redirect:/other/controller/";
and this kind of redirect is only for GET request, if you want to use other type of request use HttpServletResponse