How to create URL containing variables using Rest Template? - java

I don't know how to create URL. I want to get the value of city from User via form. AppID and app.myserviceWeatherUrl I fetch from application.yml. How to connect URL to obtain something like this: app.myserviceWeatherUrl?q=city&app.APPID ?
#Service
public class WeatherClient {
#Value("{app.APPID}")
private String appID;
#Value("{app.myserviceWeatherUrl}")
private String baseUrl;
private final RestTemplate restTemplate;
public WeatherClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public WeatherDto getWeather(String city) {
try {
return restTemplate.getForObject(baseUrl + city + appID, WeatherDto.class);
} catch (Exception e) {
throw new DataNotAvailableException();
}
}
}

You can try it like this:
import java.net.URI;
import org.springframework.web.util.UriComponentsBuilder;
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.queryParam("city", city)
.queryParam("appId", appId)
.build().toUri();
So, if baseUrl='/v1/api', city='Bern' and appId='4' it will be:
/v1/api?city=Bern&appId=4
And then pass uri to getForObject() method:
restTemplate.getForObject(uri, WeatherDto.class);

Use the Java class that is cunningly named URL.

Related

#CrossOrigin only works when register one domain

I wanted to make an connection between frontend and backend so I used #CrossOrigin annotation Like below.
#CrossOrigin(origins = "http://localhost:3000, http://server ip:3000, http://backend.com:3000")
#RestController
#RequestMapping("/member")
public class MemberController {
Logger logger = LoggerFactory.getLogger(MemberController.class);
private MemberService ms;
private Encryption encrypt;
private S3FileUploadService sfu;
#Autowired
public MemberController(MemberService ms, Encryption encrypt,S3FileUploadService sfu) {
this.ms = ms;
this.encrypt = encrypt;
this.sfu = sfu;
}
#GetMapping("/login")
public FrontMember login(#RequestHeader("Authorization") String autho) {
logger.info("Authorization : " + autho);
String memberInform = encrypt.aesDecrypt(autho);
String[] idPwd = memberInform.split("/");
Member loginTry = new Member();
loginTry.setEmail(idPwd[0]);
loginTry.setPwd(encrypt.shaEncryption(idPwd[1]));
Member authorizedUser = ms.login(loginTry);
if(authorizedUser == null){
logger.warn("No member info");
return null;
}else{
logger.info("Member Get : "+authorizedUser.toString());
String hashMemberNum = encrypt.aesEncrypt(Integer.toString(authorizedUser.getMemberNum()));
String mgHash = encrypt.aesEncrypt(Integer.toString(authorizedUser.getMg()));
FrontMember fm = new FrontMember(hashMemberNum, authorizedUser.getNickName(), authorizedUser.getPfUrl(),mgHash);
logger.info("Login User : "+fm.toString());
return fm;
}
}
}
But it doesn't work unless I only put one domain on origin like below.
#CrossOrigin(origins = "http://localhost:3000")
I want to put several domain at crossorigin.
How can I solve this problem?
Check the Official document of CrossOrign,we can found below description:
So the reason is that you have made a wrong invocation,if you need to allow multiple origins,you need to use an array contains of string instead of single string
In order to make your code work,you can try with below:
#CrossOrigin(origins = {"http://localhost:3000", "http://server ip:3000", "http://backend.com:3000"})
#RestController
#RequestMapping("/member")
public class MemberController {
}

How can I get api with FeignClient

I used Lombok, Open Feign and Spring Web
I have currencyClient interface:
#FeignClient(value = "getcurrency", url = "https://openexchangerates.org")
public interface currencyClient {
#RequestMapping(value = "/api/historical/2012-07-10.json/{smt}", method = RequestMethod.GET)
public List<Object> getCurrency(#PathVariable String smt);
}
And Controller:
#RestController
#RequiredArgsConstructor
public class StatusController {
private String appId1 = "appId";
private final currencyClient currencyClient;
#GetMapping("/getAllCurrency")
public List<Object> getCurrency(){
return currencyClient.getCurrency(appId1);
}
}
And "http://localhost:1212/getAllCurrency" is not working cause the link is converted into "**https://openexchangerates.org/api/historical/2012-07-10.json/appId**" I understand that &/= are reversed and I also think that my indication of List is not correct. That's what I tried so how can I get info from "**https://openexchangerates.org/api/historical/2012-07-10.json?app_id**" as "http://localhost:1212/getAllCurrency"?
According to the https://docs.openexchangerates.org docs, the app_id should be a request parameter (see #RequestParam), not a path variable. You could do something like this:
CurrencyClient interface:
#FeignClient(value = "getcurrency", url = "https://openexchangerates.org")
public interface CurrencyClient {
#RequestMapping(value = "/api/historical/2012-07-10.json", method = RequestMethod.GET)
Map<String, Object> getCurrency(#RequestParam("app_id") String appId);
}
StatusController:
#RestController
public class StatusController {
private final CurrencyClient currencyClient;
public MyController(CurrencyClient currencyClient) {
this.currencyClient = currencyClient;
}
#GetMapping("/getAllCurrency")
public Map<String, Object> getCurrency() {
String appId1 = "*****";
return currencyClient.getCurrency(appId1);
}
}
Some additional things to note here:
Please don't post yout API key to StackOverflow, or anywhere else publicly. Other people might abuse it. Since you already posted it, you should request a new API key and get rid of this one (close it if possible).

How to load different property value conditionally

I have a condition where I want to use the same method to be called for request coming from mobile app and request coming from webapp with different clientId and different Client secret and redirect uris,base uris .
When a request comes from mob we have header "source" value as mobile and when request comes from web we have header "source" value as web.
I want to have something like that when request comes from web ,values of client id,clientsecret,baseurl,redirecturl of web is loaded and when request comes from mobile
values of client id,clientsecret,baseurl,redirecturl of mobile is loaded.
I want to avoid to write the same logic either by creating in different method or different endpoint for mobile/web.I wan to use the same method and the same endpoint ,just that values injected from conifguration will be different based on the header value
How can I achieve this??Is there a way to #Autowire different config class based on condition
TokenController
public class TokenController {
#GetMapping("/getToken")
public ResponseEntity<?> fetchToken(#RequestParam("foo") String foo) {
ResponseEntity<?> tokenInfo = tokenInfoServiceImpl.getTokenDetails(foo);
return tokenInfo;
}}
TokenServiceImpl
public class TokenServiceImpl{
#Autowired
SSOConfig ssoConfig;
public ResponseEntity<?> getTokenDetails(String foo) {
HttpHeaders tokenExchangeHeaders = prepareRestTemplateHeader(ssoConfig.getClientId(),
ssoConfig.getClientSecret());
String baseUrl =soConfig.getBaseurl();
String redirectUrl = ssoConfig.getRedirectUrl();
//rest of the logic
}
}
SSOConfig class
#Configuration
#ConfigurationProperties(prefix = "sso.web")
public class SSOConfig {
private String baseurl;
private String redirectUrl;
private String tokenClientId;
private String tokenClientSecret;
//Getters and Setters
}
For your TokenController class, you should be able to add an argument to process a RequestHeader.
public class TokenController {
#GetMapping("/getToken")
public ResponseEntity<?> fetchToken(#RequestHeader(name = "source") String source, #RequestParam("foo") String foo) {
ResponseEntity<?> tokenInfo = null;
if ("mobile".equals(source)) {
ResponseEntity<?> tokenInfo = tokenInfoServiceImpl.getTokenDetails(foo);
} else {
//do something else
}
return tokenInfo;
}}
There's a great tutorial on this at Baeldung

Downcasting a CompletableFuture's object type - Java/Spring Boot

I have the following classes:
public class AccountDetail {
private String accountNumber;
private Date effectiveDate;
private String status;
// a bunch of other properties
}
public class AccountDetailWithAlerts extends AccountDetail {
private LowMediumAlerts alerts;
}
public class AccountsAndAlerts {
private List<AccountDetailWithAlerts> accounts;
private HighCustomerAccountAlerts accountAlerts;
// getters and setters
}
public class CustomerAndAccountAlerts {
private List<AlertMessage> customerAlerts;
private List<AccountAlertMessages> accountAlerts;
}
public Class CompanyResponse<T> {
#JsonInclude(JsonInclude.Include.NON_NULL)
private T response;
// other things that aren't relevant
}
I have a controller, AccountsController, that does a #GetMapping and has a ResponseEntity method:
public ResponseEntity<CompanyResponse<AccountsAndAlerts> getAccountDetails {
#RequestParam MultiValueMap<String, String> queryParms,
// some #ApiParams for client-header, end-user-id & accountNumber
String accountId = queryParms.getFirst("accountId");
// setting RestHeaders, contentType
CompanyResponse<AccountsAndAlerts> response = accountDetailService.getAccountsWithAlerts(restHeaders, accountNumber, queryParms, accountId);
return new ResponseEntity<CompanyResponse<AccountsAndAlerts>>(response, headers, HttpStatus.valueOf(response.getStatus()));
}
Here is the method in accountDetailService:
public CompanyResponse<AccountsAndAlerts> getAccountsWithAlerts(RestHeaders restHeaders, String accountNumber, MultiValueMap<String, String> queryParms, String accountId) throws... {
CompanyResponse<AccountsAndAlerts> newResponse = new CompanyResponse<AccountsAndAlerts>();
try {
CompletableFuture<List<AccountDetailWithAlerts>> accountsFuture = accountDetails.getAccounts(newResponse, restHeaders, accountNumber, queryParms);
CompletableFuture<CustomerAndAccountAlerts> alertsFuture = accountDetails.getAlerts(newResponse, restHeaders, accountId);
accountsFuture.thenAcceptBoth(alertsFuture, (s1, s2) -> newResponse.setResponse(getResponse(s1, s2))).get();
} catch {
// catch code
}
return newResponse;
}
Finally, the getAccounts method in AccountDetails:
public CompletableFuture<List<AccountDetailWithAlerts>> getAccounts(CompanyResponse<AccountsAndAlerts> newResponse, RestHeaders restHeaders, String accountNumber, MultiValueMap<String, String> queryParms) throws ... {
// this has the restTemplate and the .supplyAsync()
}
What I need to do is create a new ResponseEntityMethod in the Controller:
public ResponseEntity<CompanyResponse<AccountDetail> getCertainAccountDetails
I have put in a return of that type, and I am attempting to create a new method in the accountDetailService, getCertainAccounts().
The problem is trying to set this all up without creating a whole other CompletableFuture method with an invoke and supplyAsync() and restTemplate and such.
It appears that I still need to call getAccounts(), but then I have to somewhere along this line downcast the AccountDetailWithMessages to AccountDetail. I don't know if I can somehow downcast CompletableFuture<List<AccountDetailWithAlerts>> to CompletableFuture<List<AccountDetail>> or how to do it, or if I really need to downcast CompanyResponse<AccountsAndAlerts> or how to do that.
Can anyone help?
PS. I changed the names of everything to protect my Company's code. If you see errors in methods or names or anything, please be assured that is not an issue and is just the result of my typing things out instead of copying and pasting. The only issue is how to do the downcasting.
Thanks!
PPS. In case it wasn't clear, with my new method and code I do not want to get the alerts. I am trying to get account details only without alerts.

How to automatic decode Dto from GET query parameters

I Have a GET request with some parameters which I handle as an object on the controller, consider it could be several parameters.
The problem is that the values for the properties on the dto are being filled using url encoding which I dont want because it messes up queries to a database later on, ie.: name gets populated with "some%20name" instead of "some name" as I would expect.
How can I avoid this encoding problem?
Bellow is a small scenario that represents my issue:
public class SomeDto {
private String name;
private String hex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHex() {
return hex;
}
public void setHex(String hex) {
this.hex = hex;
}
}
#RestController
#RequestMapping("example")
public class RestController {
#GetMapping
public void example(final SomeDto someDto) {
System.out.println(someDto.getName());
System.out.println(someDto.getHex());
}
}
public class ClientApi {
private RestTemplate restTemplate;
private String hostUri;
public ClientApi(RestTemplate restTemplate, String hostUri) {
this.restTemplate = restTemplate;
this.hostUri = hostUri;
}
public void test(SomeDto someDto) {
var uri = UriComponentsBuilder.fromUriString(hostUri + "/example");
if(someDto != null) {
uri.queryParam("name", someDto.getName())
.queryParam("hex", someDto.getHex());
}
restTemplate.exchange(uri.toUriString(), HttpMethod.GET, null, Void.class);
}
}
#SpringBootTest(
classes = DemoApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
class ClientApiTest {
#LocalServerPort
private String port;
private ClientApi clientApi;
#BeforeEach
void before() {
clientApi = new ClientApi(new RestTemplate(), "http://localhost:" + port);
}
#Test
void testMethod() {
SomeDto someDto = new SomeDto();
someDto.setName("some name");
someDto.setHex("#ffffff");
clientApi.test(someDto);
}
}
UPDATE:
I was able to partially fix it by decoding the URL, however it only fixes name "some name" to reach the controller correctly, hex "#ffffff" on the other hand reaches as null.
var decodedUri = URLDecoder.decode(uri.toUriString(), Charset.defaultCharset());
Spring uses some symbols as service symbols.
E.g. you cannot parse param value if it contains a comma.
?someParam=some,value
Would be parsed as two params: some and value. But if receive type is not array or collection then the second value will be ignored. Hence, you'll get someParam=some.
The simplest way to avoid it is URL params base64 encoding.
For me, the convenient way was to encode params as json in Base64.
{
"name": "some name",
"hex": "fffffff"
}
Why json? Because there are many ready-made solutions for parsing JSON into an object.
So, your controller will receive Base64 value which is eyJuYW1lIjoic29tZSBuYW1lIiwgImhleCI6ImZmZmZmZmYifQ==
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Base64;
import java.util.Objects;
#RestController
public class RestController {
#GetMapping("/example")
public void example(String params) {
String decoded = decodeBase64(params);
SomeDto dto = parseTo(decodedFilters, SomeDto.class);
}
public String decodeBase64(String encoded) {
if (Objects.nonNull(encoded)) {
return new String(Base64.getDecoder().decode(encoded));
}
return "";
}
public <T> T parseTo(String jsonAsString, Class<T> classType) {
String toParse = Objects.nonNull(jsonAsString) ? jsonAsString : "{}";
try {
return new ObjectMapper().readValue(toParse, classType);
} catch (IOException e) {
throw new ValidationException(e.getMessage());
}
}
}

Categories

Resources