How to handle redirect by httpClient fluent? - httpclient

I'm writing acceptance tests with HttpClient fluent api and have some trouble.
#When("^I submit delivery address and delivery time$")
public void I_submit_delivery_address_and_delivery_time() throws Throwable {
Response response = Request
.Post("http://localhost:9999/food2go/booking/placeOrder")
.bodyForm(
param("deliveryAddressStreet1",
deliveryAddress.getStreet1()),
param("deliveryAddressStreet2",
deliveryAddress.getStreet2()),
param("deliveryTime", deliveryTime)).execute();
content = response.returnContent();
log.debug(content.toString());
}
This code works well when I use post-forward strategy, but an exception is thrown when I use redirect instead.
org.apache.http.client.HttpResponseException: Found
What I want is getting the content of the redirected page. Any idea is appreciate, thanks in advance.

The HTTP specification requires entity enclosing methods such as POST and PUT be redirected after human intervention only. HttpClient honors this requirement by default. .
10.3 Redirection 3xx
This class of status code indicates that further action needs to be
taken by the user agent in order to fulfill the request. The action
required MAY be carried out by the user agent without interaction
with the user if and only if the method used in the second request is
GET or HEAD.
...
If the 302 status code is received in response to a request other
than GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.
One can use a custom redirect strategy to relax restrictions on automatic redirection if necessary.
DefaultHttpClient client = new DefaultHttpClient();
client.setRedirectStrategy(new LaxRedirectStrategy());
Executor exec = Executor.newInstance(client);
String s = exec.execute(Request
.Post("http://localhost:9999/food2go/booking/placeOrder")
.bodyForm(...)).returnContent().asString();

This is for an updated version of apache:
CloseableHttpClient httpClient =
HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy())
.build();

Related

cant set Authorization Header in HttpClient ( .Net core 3.1 )

I am migrating from .net core 2.2 to 3.1. I am making an XUnit test method to test my controllers.
I successfully made and tested in .net core 2.2 projects, but after migrating to 3.1 it seems it cant set authorization header to my request so I am getting UnAuthorized from my app.
this is my Code :
[Fact]
public async void InvalidId_UnSuccessFull_GetById()
{
// Arrange
var httpClient = new HttpClient();
var token = await GetAdminAccessToken(); // Sends a login request and fetch a valid token
// httpClient.DefaultRequestHeaders.Add("Authorization",$"Bearer {token}");
httpClient.DefaultRequestHeaders.Authorization=new AuthenticationHeaderValue("Authorization",$"Bearer {token}");
var id = Guid.Empty;
// Act
var response = await httpClient.GetAsync("localhost:5000/Admin/User/{id}");
var message = await ExtractMessage(response);
// Assert
Assert.Contains(PersianErrorMessage.InvalidUserId, message);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
I debugged into httpClient class till the sendAsync method and the HttpRequestMessage request instance does not have an Authorization Header that I set above! What's wrong with my code?
The AuthenticationHeaderValue should be set like this:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Source: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.headers.authenticationheadervalue.-ctor?view=netcore-3.1#System_Net_Http_Headers_AuthenticationHeaderValue__ctor_System_String_
There are a couple of issues that could trip you up here. This can be a frustrating issue, so hopefully, this helps. The HTTP client is more than likely sending the Authorization header. If there is any kind of redirect, the Authorization header does not travel with it. I ran into this issue once and the only problem was my original URL did not have a trailing slash at the end.
What I entered:
https://api.example.com/v3/endpoint
Server endpoint:
https://api.example.com/v3/endpoint/
Step 1, Determine if there is a redirect.
Step 2, Make your URL match the final URI exactly
Clients like curl do this automatically. In .Net 3.1, you will have to make sure your client checks for redirects. A 401 error is actually a good thing--it means you more than likely reached the right endpoint albeit without the authorization token.
You can do this in code, but just run debug and break after you get the response. Examine the RequestURI property of the RequestMessage object of the HttpResponseMessage (response.RequestMessage.RequestURI). Compare this with your initial URL. If they do not match exactly, you've been redirected and the Authorization header was lost along the way.

Spring Cloud Gateway Routing Based On Content of the Request Body

I need to create a reverse proxy that takes incoming request and based on the content of the request body, route the request to specific URI.
This is for a routing micro service that acts like a reverse proxy and does routing based on some information from each request body. This means for each request I need to parse the request body and get the "username" field and then make a JDBC connection to fetch additional information from the database. Based on that information in database, it would finally redirect the request to the correct URI.
From what I have now, I have 2 blocking methods. The first one is the parsing for the request body, the other one is the JDBC connection to the database. I understand that I should not put any blocking calls inside the gateway filter. I just don't know what I should do in this case. I could have both operations running async but in the end I still need the information from database to do routing.
#Bean
public RouteLocator apiLocator(RouteLocatorBuilder builder, XmlMapper xmlMapper) {
return builder.routes()
.route(r -> r
.path("/test")
.and()
.readBody(String.class, s -> true) // Read the request body, data will be cached as cachedRequestBodyObject
.filters(f -> f.filter(new GatewayFilter() {
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
// The following method is blocking and should not be put here
xmlMapper.readValue((String) exchange.getAttribute("cachedRequestBodyObject"), Map.class);
} catch (Exception e) {
//TODO
}
return chain.filter(exchange);
}
}))
.uri("http://localhost:8080"))
.build();
}
The above example only includes the blocking parsing as my request body is XML based. My IDE is warning me of having a blocking call there which I really appreciate.
Any help is greatly appreciated. Thank you everyone!
After some research, Mono.fromCallable seems to be a good fit. I then asked the same question directly under the github repo, it turns out that using a servlet app may be better. For anyone who is interested to see what I came up with, please take a look here https://github.com/spring-cloud/spring-cloud-gateway/issues/1229

How to Pass object to REST Get Method

I am using Jersey Rest implementation. There are one Rest Services Called HelloWorld. See the below code.
Please consider this code as reference not as compiled code.
#Path("helloWorld")
public class HelloWorld{
#Path("test")
#Produces(...)
#Consum(...)
#GET
public Response test(Person person){
System.out.println(person);
}
}
I am using Jersey client to sent the request.
Here My question is apart from POST method is there any way to send the object to GET method directly. Instead of QueryString.
Please let me if there is any way to do so.
Thanks
So the problem shouldn't be with the server. I did a few tests on different servers (not weblogic as I don't use it) and all of them seem to have no problems accepting a body in the GET request. The problem seem to be with the client. To test I used the following code
ClientBuilder.newClient()
.target("http://localhost:8080/api/get-body")
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.request()
.method(HttpMethod.GET, Entity.text("Hello World"));
The SUPPRESS_HTTP_COMPLIANCE_VALIDATION allows us to pass a body to the request. If we didn't use this, then we would get an error.
The problem with this code, is that even though we set this override property, the client completely overrides the GET method and automatically makes it a POST method, so I would get back a 405 Method Not Allowed.
The solution I came up with is to just allow the client to set a header, e.g. X-GET-BODY-OVERRIDE, and then use a #PreMatching filter on the server side to check for this header. If the header is present, then just change the method to a GET
#Provider
#PreMatching
public class GetWithBodyFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext request) throws IOException {
String getOverride = request.getHeaderString("X-GET-BODY-OVERRIDE");
if (getOverride != null && "true".equalsIgnoreCase(getOverride)) {
request.setMethod(HttpMethod.GET);
}
}
}
Then just register the filter with the server side. On the client, you would simply need to add the header
ClientBuilder.newClient()
.target("http://localhost:8080/api/get-body")
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.request()
.header("X-GET-BODY-OVERRIDE", "True")
.method(HttpMethod.GET, Entity.text("Hello World"));
This solution is good because it takes into account more than just the Jersey client, in regards with being able to send a body in the GET request.

How to enable Cross Origin Resource Sharing (CORS) in a Fantom / afBedSheet REST service?

I am developing a REST API with Fantom and afBedSheet. I need to allow cross-origin resource sharing so that I can call my RESTful services via AJAX from the UI which runs on a different web container on a different port.
I am currently doing this in request handler methods:
res.headers["Access-Control-Allow-Origin"] = "http://localhost:8080"
But as the API grows and the number of request handlers grow, it is no longer practical. I'm wondering how can I inject that header in every response. I have Googled the question but only found a reference to a document from a very old version of afBedSheet which doesn't seem relevant anymore. Can anyone provide an example, please?
CORS has to be set up manually but as mentioned, it's not that difficult. Anything that becomes repetitive in request handler methods can usually be squirrelled away somewhere, and setting HTTP response headers is no different. These can be set via BedSheet Middleware:
using afIoc
using afBedSheet
const class CorsMiddleware : Middleware {
#Inject private const HttpRequest req
#Inject private const HttpResponse res
#Inject private const ResponseProcessors processors
new make(|This|in) { in(this) }
override Void service(MiddlewarePipeline pipeline) {
// echo back in the response, whatever was sent in the request
res.headers["Access-Control-Allow-Origin"] = req.headers["Origin"]
res.headers["Access-Control-Allow-Methods"] = req.headers["Access-Control-Request-Method"]
res.headers["Access-Control-Allow-Headers"] = req.headers["Access-Control-Request-Headers"]
// deal with any pre-flight requests
if (req.httpMethod == "OPTIONS")
processors.processResponse(Text.fromPlain("OK"))
else
pipeline.service
}
}
Note that the above will enable CORS on all requests - handy for dev, but for live code you should be more choosy and validate any given Origins, Methods, and Headers.
BedSheet Middleware should be contributed to the MiddlewarePipeline service:
#Contribute { serviceType=MiddlewarePipeline# }
static Void contributeMiddleware(Configuration config) {
config.set("myApp.cors", config.autobuild(CorsMiddleware#)).before("afBedSheet.routes")
}
Note that CorsMiddleware is inserted into the pipeline before BedSheet routes to ensure it gets executed.

How to change which resources answers a request from Servlet Filter?

I have a ServletFilter (which happens to be a GuiceShiroFilter) that processes incoming web requests before they go to a Jersey 1.x Resource.
However, in some situations (namely when Shiro finds that the request is not authenticated), I want to change which Jersey resource answers my request, without the resource that otherwise would have answered even being able to respond.
Here's what I have (in my Shiro AuthenticatingFilter.onLoginFailure()):
ServletRequest request = ...;
RequestDispatcher disp = request.getRequestDispatcher("/resource/that/always/responsds/with/a/403");
try {
disp.forward(request, response);
} catch (ServletException | IOException e) {
e.printStackTrace();
}
// this is needed to prevent the woud-be resource from responding as well
return false;
The problem of this server-side redirect is that not returning false will invoke both my resource for "/resource/that/always/responsds/with/a/403" and the original would-be response, and in the best case the response body contains both responses concatenated.
Is there a way to modify an existing instance of (Http)ServletRequest from a Filter such that later on, only the redirected-to resource can answer?
I dug into the issue a little bit and I realized that there is little I can do here, given that I don't control how the filter chain is processed further.
In a generic Filter, the implementer can wrap the current ServletRequest (in doFilter() in a HttpServletRequestWrapper implementation. This wrapper can then override the request URI. However, this requires that the implementer has control over how the filter chain is continued (which is where the wrapped request can be fed back into the execution path), but this is not the case in my situation, where Shiro controls that.
With Shiro, the filter chain continuation is controlled in AdviceFilter.doFilterInternal(), many layers above my own AuthenticatingFilter implementation.
So for now, my best bet is to do what I already described above: Invoke another resource - free from filters - by using a RequestDispatcher and stop the filter chain by returning false at the end of my AuthenticatingFilter's onLoginFailure()