I've tried to add a custom HttpClient via configuration:
#Bean
public CloseableHttpClient httpClient() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(15000)
.setConnectionRequestTimeout(15000)
.build();
Header header = new BasicHeader("Test", "Test");
Collection<Header> headers =Arrays.asList(header);
return HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setDefaultHeaders(headers)
.build();
}
but still, my custom added default header doesn't appear in the request.
My Feign client interface looks like below:
#FeignClient(name = "example",
url = "${client.example.api}",
decode404 = false,
configuration = FeignClientConfiguration.class)
public interface ExampleFeignProxy{
#PostMapping(path = "/create")
#Headers("Content-Type: application/json")
String Create(
#RequestBody ExampleDTO exampleDto,
#RequestHeader("access-token") String token);
}
but when I make request to the Create method, request fails, when I inspect inside configuration.errordecoder, it shows feign is adding an extra header Content-Length also to the request.
How can I remove default headers from all methods inside my feign client?
To make it clear - as shown above, only two headers should have been present on the request object
Content-Type
Access-Token
but Feign somehow adds Content-Length as well.
Is there a configuration somewhere I need to set?
Actually, it was a misunderstanding, above configuration was always working, I was not parsing the error properly. The error returned was actually from the api.
All I had to do was to properly specify the errordecoder.
Related
I'm developing a C# application that needs to contact a web-based API. When contacting the API, the first thing it does is try to get an authorization code from an authorization server. Using RestSharp, my code is this:
static string GetAuthCode(string authUri, string clientId, string scope, Guid state, string callbackUri)
{
var client = new RestClient(authUri);
var request = new RestRequest("", Method.Post);
client.Options.MaxTimeout = -1;
request.AddParameter("client_id", clientId);
request.AddParameter("response_type", "code");
request.AddParameter("scope", scope);
request.AddParameter("state", state);
request.AddParameter("redirect_uri", callbackUri);
RestResponse response = client.Execute(request);
if (response.IsSuccessful)
{
string code = HttpUtility.ParseQueryString(response.ResponseUri.Query).Get("code");
return code;
}
else
throw new Exception(response.Content);
}
When I call this method, the response is successful, however I was expecting that the resulting authorization code would be appended to the ResponseUri property of the response (in its Query property). But it's not. The ResponseUri property is set to the authorization Uri (authUri). Am I looking in the wrong spot for the authorization code? Where can I find the actual authorization code?
It should be in the query parameters:
If the resource owner grants the access request, the authorization
server issues an authorization code and delivers it to the client by
adding the following parameters to the query component of the
redirection URI using the "application/x-www-form-urlencoded" format,
per Appendix B:
4.1 Authorization Code Grant - 4.1.2 Authorization Response
I'm trying to trasform http GET method call from legacy api server built with MVC1 pattern to new restful api server without any change of front-end source code using netflix zuul and eureka.
I added zuul pre filter transforming legacy url to restful convention url working after PreDecorationFilter and it works fine.
But now I'm facing problem converting the GET method to proper method like POST, PUT, DELETE by distinguising url so that the requests are properly mapped in spring controller via #GetMapping/#PostMapping/#PutMapping/#DeleteMapping.
I looked into SimpleRoutingFilter that handles HttpClient but
Because of environmental constraint, I have to use eureka service id to route to the new api server and that means I should use RibbonRoutingFilter which is quite complicated to find out a right place to this operation in.
So, is this possible to change http method or make new http request before RibbonRoutingFilter?
If possible can you please suggest where is the right place to do that or some reference?
Many thanks!
======================================================================
Milenko Jevremovic,
Would you please tell me more detail about using Feign?
I defiend #FeignClient like below
#PostMapping(value = "{url"}, consumes = "application/json")
ResponseEntity<?> postMethod(#PathVariable("url") String url);
and to get query parameters to request body for POST In zuul pre filter,
after transform logic from GET request url to POST new restful url ...
byte[] bytes = objectMapper.writeValueAsBytes(ctx.get("requestQueryParams"));
ctx.setRequests(new HttpServletRequestWrapper(request) {
#Override ..getMethod
#Override ..getContentLength
#Override ..getConentLengthLong
#Override
public ServletInputStream getInputStream() {
return new ServletInputStreamWrapper(bytes);
}
}
ResponseEntity<?> response feignClient.post(transformedNewApiUri);
and set RequestContext code that you suggested ....
and controller of new api server is like,
#PostMapping
ResponseEntity<model> post(#RequestBody req..)
It comes to controller fine but when I see the http request in post method of controller,
There is no request body for parameters.
(HttpServleterRequest getInputStream shows empty)
The request data set in zuul pre filter by HttpServletRequestWrapper is
not used in Feign maybe...?
Would you please get me more idea setting request body when changing GET query
to POST constructor for using Feign?
It is not possible to change method of HttpServletRequest, but it's possible to replace request in RequestContext. HttpServletRequestWrapper appears to be very helpful:
static class PostHttpServletRequest extends HttpServletRequestWrapper {
public PostHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public String getMethod() {
return "POST";
}
}
So method run can be rewritten as following:
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletRequest requestWrapper = new PostHttpServletRequest(request);
ctx.setRequest(requestWrapper);
return null;
}
After doing some research did not find any built in solution.
But what comes in my mind you can use Feign client in your Pre filter, get the response, set the response and return it immediately to client from your Pre filter.
You can set Feign client url or your service id, like it is explained in the docs, it uses ribbon as well .
Change response in your run method like:
...
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setResponseStatusCode(your_code);
ctx.setResponseBody(new_body);
ctx.setSendZuulResponse(false);
return null
I need to upload file through zuul to my application, the content type is multipart/form-data, but I have some other parameters in the request.
So how can I get the parameters in the zuul?
I want to get the token to check the request is valid or not.
I've tried "request.getParameter("token");",but it does not work.
if you Get request through
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
the call request.getParameter("token"); It does not return any valid values
This away may solve this issue
HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) request;
String token = httpServletRequestWrapper.getRequest().getParameter("token");
ps:
I suggest you check the request type first before getting the value.by this way
String requestType = request.getContentType().split(";")[0];
if(MediaType.MULTIPART_FORM_DATA_VALUE.equls(requestType)){
HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) request;
token = httpServletRequestWrapper.getRequest().getParameter("token");
}
When using HttpClient, I have read examples (such as this example) that uses DefaultRequestHeaders to set the content type (such as "application/json") of a Post request.
I tried to do something like this, but it failed. The API I am sending requests complained that it was sent an "unsupported type" (which it says when the content type is not set to json).
After that I added one line and I solved the issue (you can see the line in the code below commented).
My question is why is this line necessary? And if I include this line (that is setting the content type of the content) doesn't that make the "default request header" setting unnecessary. What is this "default request header" doing if anything?
(I actually tried and commented the lines related to DefaultRequestHeaders and it worked without problem. So what is DefaultRequestHeaders good for?)
My code is :
// Get the bytes for the request, should be pre-escaped
byte[] bytes = Encoding.UTF8.GetBytes(jsonEmployeeData);
client.BaseAddress = new Uri("the address here");
// client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("customHeader", "blahblahblah");
ByteArrayContent byteContent = new ByteArrayContent(bytes); //Make a new instance of HttpContent (an abstract class that can't be instantiated)
//THIS is the solution
//byteContent.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json"); //If I UNCOMMENT THIS, IT WORKS!!
try
{
HttpResponseMessage response = await client.PostAsync("staff", byteContent);
Console.WriteLine(response.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Something happened, oopps!" + ex.Message);
}
Console.WriteLine("Press any key");
Console.ReadLine();
client is a HttpClient by the way.
The API you called needs the request content is application/json format, so you have to specify this format in your content. That's why that line of code is the one you need:
byteContent.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");
About the line:
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"))
This is request Accept header, it means the content type of response which you expect the server return to you. (You can expect server returns another content type like text/plain,...). Hope this helps!
How to set session attributes in restassured? In my application code we have something like this
String userId= request.getSession().getAttribute("userid")
How to set userId as session attribute here(in restassured test case)?
How to maintain the same session for all the requests(multiple subsequent requests)?
When i send multiple requests, it's considering every request as new and session is getting invalidated from server side, i want to maintain session between subsequent calls.
I tried setting jsessionid in the cookie and sent it in the second request, but when i debugged in the server side, it's not loading the session which was created, instead it's creating different session and because of this its doesn't show the attribute which i have set in the session when i first sent the request.
When i tried the same with direct HttpClient, it working, where as the same with RestAssured it's not working.
Code which was working with HttpClient is this
HttpClient httpClient = util.getHttpClient();
//1st request
HttpResponse response=httpClient.execute(postRequest);
from response i have extracted the jessionid and set this in the second request
HttpGet getRequest = new HttpGet(Client.endPointUrl);
getRequest.addHeader("content-type", "application/json");
getRequest.addHeader("accept", "application/json");
getRequest.addHeader("Origin", Client.endPointUrl);
getRequest.addHeader("Referer", Client.endPointUrl);
getRequest.addHeader("Auth-Token", authToken);
getRequest.addHeader("Set-Cookie", jsessionId);
//2nd request after setting the jessionid which i have extracted from the response
HttpResponse eventsResponse = httpClient.execute(getRequest);
Above code is working perfectly fine and i am getting expected response. One observation is i am using the same httpClient Object for invoking both the requests.
Where as i if i try the same using RestAssured, it's not working.
RestAssured.baseURI = "http://localhost:8080";
Response response=RestAssured.given().header("Content-Type","application/json").
header("Origin","http://localhost:8080").
header("Referer","http://localhost:8080").
body("{"+
"\"LoginFormUserInput\":{"+
"\"username\":\"test\","+
"\"password\":\"password\""+
"}"+
"}")
.when().post("/sample/services/rest/validateLogin").then().extract().response();
JsonPath js=Util.rawToJson(response);
String sessionId=js.get("sessionID");
System.out.println(sessionId);
for (Header header:response.getHeaders()) {
if ("Set-Cookie".equals(header.getName())) {
id= header.getValue().split(";")[0].trim();
String[] arr=jsessionId.split("=");
jsessionId=arr[0];
break;
}
}
response=RestAssured.given().header("Auth-Token",sessionId).header("Content-Type","application/json").
cookie("JSESSIONID",jsessionId).
header("Origin","http://localhost:8080").
header("Referer","http://localhost:8080").
body("{}").
when().
post("/sample/services/rest/getAllBooks").then().contentType("").extract().response();
I tried reusing the same httpclient for all the requests using the following, but it didn't work
RestAssured.config = RestAssured.config().httpClient( new HttpClientConfig().reuseHttpClientInstance());
You need to use Session filter in Rest Assured
https://github.com/rest-assured/rest-assured/wiki/Usage#session-support