How to retrieve the API Gateway URI upon post - uri

I'm developing a system in a microservices architecture using java8 and Spring Cloud. I implemented a post rest controller that receives an object in json and then saves it in the database. The problem is; how do I get the URI containing the API Gateway of the just saved object so I can return it on the created responde body?
Like, when I use
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(savedProfile.getId()).toUri();
I get http://Boss.mshome.net:8081/profile/1 instead of localhost:8765/nukr-profile-service/profile/1 which is the endpoint with the API Gateway's path.
What's the best practice to retrieve this URI?

So, I came to a solution I just don't know if it's best practice or not.
My post method ended up being like:
#PostMapping("/profile")
public ResponseEntity<Object> createProfile(#Valid #RequestBody Profile profile) {
UriComponents requestComponents = ServletUriComponentsBuilder.fromCurrentRequest().path("").build();
InstanceInfo gatewayService = this.eurekaClient.getNextServerFromEureka(NUKR_API_GATEWAY_SERVICE, false);
Profile savedProfile = this.profileRepository.save(profile);
String uriStr = requestComponents.getScheme() + "://" + gatewayService.getIPAddr() + ":" + gatewayService.getPort() + "/" + this.profileServiceName +
requestComponents.getPath() + "/" + savedProfile.getId();
return ResponseEntity.created(URI.create(uriStr)).build();
}

Related

Karate - Authentication - cannot access url address under password

Using Karate, I have need to use basic authentication (to pass common authentication dialog window with username and password), and I have tried this: https://github.com/intuit/karate#http-basic-authentication-example).
I have created the file basic-auth.js
function fn(creds) {
var temp = creds.username + ':' + creds.password;
var Base64 = Java.type('java.util.Base64');
var encoded = Base64.getEncoder().encodeToString(temp.bytes);
return 'Basic ' + encoded;
}
I have added the call to the test feature file I run (added to Scenario section):
header Authorization = call read('basic-auth.js') { username: 'realusernamestring', password: 'realpasswordstring' }
Then I have placed the url I want to access right after:
driver urlUnderPassword
But it did not work, I still cannot access the page. I think there is something missing, something what needs to be done. Could you help me what the problem might be?
Thank you.
What you are referring to is for API tests not UI tests.
If you need the browser / driver to do basic auth it should be easy, just put it in the URL: https://intellipaat.com/community/10343/http-basic-authentication-url-with-in-password
So I am guessing something like this will work:
* driver 'http://' + username + ':' + password + '#' + urlUnderPassword

how can i send a post request via Business Central?

I would like to create a PostRequest in my Business Central Extension that authenticates me in my web service and returns me a token. I send my username and password in the body of the request to my web service and I also receive the token in JSON format in the body.I want to create the post request using HttpClient.
I use the following link as a template: https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/httpcontent/httpcontent-data-type
procedure sendPostRequest(uri: Text)
var
client: HttpClient;
content: HttpContent;
contentHeaders: HttpHeaders;
response: HttpResponseMessage;
request: HttpRequestMessage;
begin
content.GetHeaders(contentHeaders);
contentHeaders.Clear();
contentHeaders.Add('Content-Type', 'application/json');
request.Content:= content;
request.SetRequestUri(uri);
request.Method := 'POST';
end;
procedure SetURLsToDefault(var MessagingServiceSetup: Record "Messaging Service Setup WMR")
begin
MessagingServiceSetup."Service URL" := '202.212.127:8800';
end;
And I have a couple of questions:
1) the basic url is 202.212.127:8800 for my API gateway. To be able to authenticate myself I have to access 202.212.127:8800/authenticate. Is there a method in which you can create urls?
2) how do I get my username and password in the content?
3) and how do I get the token and can I save it in the field?
can someone tell me how to get the PostRequest up and running?
Common method to create different URLs is like this:
Create a setup table
Create fields like "Base Url", User, Pass etc.
I propose this pattern for your code:
SendRequest(Method; Url; Body)
Begin
...
Couple of functions (Your Api and Auth):
Authenticate()
begin
Method = 'post';
Url = SetupTable."Base Url" + '/authenticate';
Body = (Use AL Json stack and incorporate your user pass)
SendRequest(Method; Url; Body);
end;
Function1()
begin
Method = 'get';
Url = SetupTable."Base Url" + '/apiPath-Function1';
Body = '';
SendRequest(Method; Url; Body);
end
Function2()
begin
Method = 'post';
Url = SetupTable."Base Url" + '/apiPath-Function2';
Body = (Use AL Json stack and incorporate your body structure);
SendRequest(Method; Url; Body);
end;
To get your user pass into the content you need to check the documentation of the Api you're trying to call. It's usually described in details, it can be a simple header for basic authentication or a complex Jwt.
For receiving a token, again you need to check your Api documentation first, but essentially after making a Rest call (like: client.Send(RequestMessage, ResponseMessage); inside your SendRequest method), you get a response back and you can use AL Json stack to carve information out.
This is a fine article on how to proceed:
https://jackmallender.com/2019/03/04/interacting-with-rest-apis-using-json-from-within-business-central-part-1-an-introduction-to-the-httpclient-data-type/
Basically a string could work as an url. Depends on what you want. It is good practice to have a setup for your web service calls, so I am with Babak. You can set up a table in which you store the links, credentials - whatsoever.
and 4) I suggest Waldos Rest App for web service calls. you can download the source here: https://github.com/waldo1001/waldo.restapp
It encapsulated the calls, has helper functions for json handling as well. Using the "REST Helper" Codeunit. You can break down your call to:
local procedure DoCallWebservice(URI: Text; User: Text; Pass: Text; var Token: Text);
var
RESTHelper: Codeunit "REST Helper WLD";
begin
RRESTHelper.Initialize('GET', URI);
RESTHelper.SetContentType('application/json');
RESTHelper.AddBody('{"user":"USERNAME","pass":"PASSWORD"}');
if RESTHelper.Send() then
Token := RESTHelper.GetResponseContentAsText();
end;
Obviously, you need to parse the response (JSONHelper) to your needs. Look at the code of the codeunit, it's more or less self explanatory.

spring MVC controller versioning

I have a spring boot application , which have a spring MVC controller. I am trying to version my rest api using Accept header.
The following is how my Controller looks like
RestController
#RequestMapping(value = "/private/")
public class AppleController {
private final AppleService appleService;
public AppleController(AppleService appleService) {
this.appleService = appleService;
}
#GetMapping(value = "apples/{id}", produces = "application/json; v=1.0",
headers = "Accept=application/json; v=1.0")
public ResponseEntity getByappleId(#PathVariable("id") Long appleId) {
System.out.println("version1");
GetByappleIdResponse response = appleService.findByappleId(appleId);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#GetMapping(value = "apples/{id}", produces = "application/json; v=2.0",
headers = "Accept=application/json; v=2.0")
public ResponseEntity getByappleId2(#PathVariable("id") Long appleId) {
System.out.println("version2");
GetByappleIdResponse response = appleService.findByappleId2(appleId);
return new ResponseEntity<>(response, HttpStatus.OK);
}
Irrespective of the version that I am passing in the Accept header when calling the API always "getByappleId" method is called, hence only version 1 response is returned.
Is there anything wrong in my controller ?
There are many options to implement versioning of REST API:
suggested in the comments approach for manually routing your request;
making version as a part of your Accept header value, f.e.:
(headers = "Accept=application/vnd.name.v1+json")
(headers = "Accept=application/vnd.name.v2+json")
making version as a part of your mapping:
#GetMapping("apples/v1/{id})"
#GetMapping("apples/v2/{id})
So you need to decide which way to go. Some useful links:
Versioning a REST API
Best practices for API versioning?
As described in this answer: https://stackoverflow.com/a/34427044/258813 (and mentioned in the comments) Spring does not support routing using the headers like that.
If you want to support routing via a version header, I would recommend a custom routing condition and annotation - certainly if you are building a large API, it will result in less code and a more elegant solution.
You would define some annotation like #ApiVersion(1) that you can add to any method that is also a request mapping and then add the custom routing condition and it will behave correctly.
I have described using custom routing conditions and annotations (based on subdomains - but that could easily be switched to check headers instead) here: http://automateddeveloper.blogspot.co.uk/2014/12/spring-mvc-custom-routing-conditions.html

ASP.Net Web API - Authorization header blank

I am having to re-write an existing REST API using .NET (originally written with Ruby). From the client's perspective, it has to work exactly the same way as the old API - i.e. the client code mustn't need to change. The current API requires Basic Authentication. So to call the old API, the following works perfectly:-
var wc = new System.Net.WebClient();
var myCache = new CredentialCache();
myCache.Add(new Uri(url), "Basic", new NetworkCredential("XXX", "XXX"));
wc.Credentials = myCache;
var returnBytes = wc.DownloadData("http://xxxx");
(I have had to ommit the real URL / username / password etc for security reasons).
Now I am writing the new API using ASP.Net Web API with MVC4. I have a weird problem and cannot find anybody else with exactly the same problem. In order to support Basic Authentication, I have followed the guidelines here:
http://sixgun.wordpress.com/2012/02/29/asp-net-web-api-basic-authentication/
One thing, I put the code to "hook in the handler" in the Global.asax.cs file in the Application_Start() event (that wasn't explained so I guessed).
Anyway, if I call my API (which I have deployed in IIS) using the above code, the Authorization header is always null, and the above fails with 401 Unauthorized. However, if I manually set the header using this code, it works fine - i.e. the Authorization header now exists and I am able to Authenticate the user.
private void SetBasicAuthHeader(WebClient request, String userName, String userPassword)
{
string authInfo = userName + ":" + userPassword;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
request.Headers["Authorization"] = "Basic " + authInfo;
}
.......
var wc = new System.Net.WebClient();
SetBasicAuthHeader(request, "XXXX", "XXXX");
var returnBytes = wc.DownloadData("http://xxxx");
Although that works, it's no good to me because existing users of the existing API are not going to be manually setting the header.
Reading up on how Basic Authentication works, the initial request is meant to be anonymous, then the client is returned 401, then the client is meant to try again. However if I put a break point in my code, it will never hit the code again in Antony's example. I was expecting my breakpoint to be hit twice.
Any ideas how I can get this to work?
You're expecting the right behavior. System.Net.WebClient does not automatically include the Authorization headers upon initial request. It only sends them when properly challenged by a response, which to my knowledge is a 401 status code and a proper WWW-Authenticate header. See here and here for further info.
I'm assuming your basic authentication handler is not returning the WWW-Authenticate header and as such WebClient never even attempts to send the credentials on a second request. You should be able to watch this in Fiddler or a similar tool.
If your handler did something like this, you should witness the WebClient approach working:
//if is not authenticated or Authorization header is null
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
response.StatusCode = HttpStatusCode.Unauthorized;
response.Headers.Add("WWW-Authenticate", "Basic realm=\"www.whatever.com\"");
return response;
});
//else (is authenticated)
return base.SendAsync(request, cancellationToken);
As you noticed, if you include the Authorization headers on every request (like you did in your alternative approach) then your handler already works as is. So it may be sufficient - it just isn't for WebClient and other clients that operate in the same way.

Get Root/Base Url In Spring MVC

What is the best way to get the root/base url of a web application in Spring MVC?
Base Url = http://www.example.com or http://www.example.com/VirtualDirectory
I prefer to use
final String baseUrl =
ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
It returns a completely built URL, scheme, server name and server port, rather than concatenating and replacing strings which is error prone.
If base url is "http://www.example.com", then use the following to get the "www.example.com" part, without the "http://":
From a Controller:
#RequestMapping(value = "/someURL", method = RequestMethod.GET)
public ModelAndView doSomething(HttpServletRequest request) throws IOException{
//Try this:
request.getLocalName();
// or this
request.getLocalAddr();
}
From JSP:
Declare this on top of your document:
<c:set var="baseURL" value="${pageContext.request.localName}"/> //or ".localAddr"
Then, to use it, reference the variable:
Go Home
You can also create your own method to get it:
public String getURLBase(HttpServletRequest request) throws MalformedURLException {
URL requestURL = new URL(request.getRequestURL().toString());
String port = requestURL.getPort() == -1 ? "" : ":" + requestURL.getPort();
return requestURL.getProtocol() + "://" + requestURL.getHost() + port;
}
Explanation
I know this question is quite old but it's the only one I found about this topic, so I'd like to share my approach for future visitors.
If you want to get the base URL from a WebRequest you can do the following:
ServletUriComponentsBuilder.fromRequestUri(HttpServletRequest request);
This will give you the scheme ("http" or "https"), host ("example.com"), port ("8080") and the path ("/some/path"), while fromRequest(request) would give you the query parameters as well. But as we want to get the base URL only (scheme, host, port) we don't need the query params.
Now you can just delete the path with the following line:
ServletUriComponentsBuilder.fromRequestUri(HttpServletRequest request).replacePath(null);
TLDR
Finally our one-liner to get the base URL would look like this:
//request URL: "http://example.com:8080/some/path?someParam=42"
String baseUrl = ServletUriComponentsBuilder.fromRequestUri(HttpServletRequest request)
.replacePath(null)
.build()
.toUriString();
//baseUrl: "http://example.com:8080"
Addition
If you want to use this outside a controller or somewhere, where you don't have the HttpServletRequest present, you can just replace
ServletUriComponentsBuilder.fromRequestUri(HttpServletRequest request).replacePath(null)
with
ServletUriComponentsBuilder.fromCurrentContextPath()
This will obtain the HttpServletRequest through spring's RequestContextHolder. You also won't need the replacePath(null) as it's already only the scheme, host and port.
request.getRequestURL().toString().replace(request.getRequestURI(), request.getContextPath())
Simply :
/*
* Returns the base URL from a request.
*
* #example: http://myhost:80/myapp
* #example: https://mysecuredhost:443/
*/
String getBaseUrl(HttpServletRequest req) {
return ""
+ req.getScheme() + "://"
+ req.getServerName()
+ ":" + req.getServerPort()
+ req.getContextPath();
}
In controller, use HttpServletRequest.getContextPath().
In JSP use Spring's tag library: or jstl
Either inject a UriCompoenentsBuilder:
#RequestMapping(yaddie yadda)
public void doit(UriComponentBuilder b) {
//b is pre-populated with context URI here
}
. Or make it yourself (similar to Salims answer):
// Get full URL (http://user:pwd#www.example.com/root/some?k=v#hey)
URI requestUri = new URI(req.getRequestURL().toString());
// and strip last parts (http://user:pwd#www.example.com/root)
URI contextUri = new URI(requestUri.getScheme(),
requestUri.getAuthority(),
req.getContextPath(),
null,
null);
You can then use UriComponentsBuilder from that URI:
// http://user:pwd#www.example.com/root/some/other/14
URI complete = UriComponentsBuilder.fromUri(contextUri)
.path("/some/other/{id}")
.buildAndExpand(14)
.toUri();
In JSP
<c:set var="scheme" value="${pageContext.request.scheme}"/>
<c:set var="serverPort" value="${pageContext.request.serverPort}"/>
<c:set var="port" value=":${serverPort}"/>
base url
reference https://github.com/spring-projects/greenhouse/blob/master/src/main/webapp/WEB-INF/tags/urls/absoluteUrl.tag
#RequestMapping(value="/myMapping",method = RequestMethod.POST)
public ModelandView myAction(HttpServletRequest request){
//then follow this answer to get your Root url
}
Root URl of the servlet
If you need it in jsp then get in in controller and add it as object in ModelAndView.
Alternatively, if you need it in client side use javascript to retrieve it:
http://www.gotknowhow.com/articles/how-to-get-the-base-url-with-javascript
I think the answer to this question: Finding your application's URL with only a ServletContext shows why you should use relative url's instead, unless you have a very specific reason for wanting the root url.
If you just interested in the host part of the url in the browser then directly from request.getHeader("host")) -
import javax.servlet.http.HttpServletRequest;
#GetMapping("/host")
public String getHostName(HttpServletRequest request) {
request.getLocalName() ; // it will return the hostname of the machine where server is running.
request.getLocalName() ; // it will return the ip address of the machine where server is running.
return request.getHeader("host"));
}
If the request url is https://localhost:8082/host
localhost:8082
Here:
In your .jsp file inside the [body tag]
<input type="hidden" id="baseurl" name="baseurl" value=" " />
In your .js file
var baseUrl = windowurl.split('://')[1].split('/')[0]; //as to split function
var xhr = new XMLHttpRequest();
var url='http://'+baseUrl+'/your url in your controller';
xhr.open("POST", url); //using "POST" request coz that's what i was tryna do
xhr.send(); //object use to send```

Resources