英文:
Is routing API calls through my own RESTful API considered an acceptable strategy?
问题
这个问题可能被认为是主观的,但我确实找不到一个明确的答案。要么我漏掉了什么,要么我问错了问题。
所以,我是一名本科生,对整个Spring应用程序开发都很新奇,目前正在创建一个使用React作为前端,并使用Spring构建RESTful API的应用程序,以支持后端所需的操作。在其他服务之中,我正在构建的后端API被用作中间人,将调用转发到Google Geocoding API和其他第三方API。自从我开始这个项目以来,我最大的问题是,这是否是一个有效的策略?(至少,是否被行业专业人士接受?)
我的研究
- 这种方法最大的优点是,它允许我有效地隐藏我的API密钥,从而使任何攻击都不可能。
- 与此同时,我相信整个过程增加了不必要的复杂性和延迟,可能会导致用户体验受到影响。
- 我还不太确定的是,我将不得不为我的API暴露的服务添加
async
功能,以便支持多个用户。(最后这一部分可能完全错误,但我还没有能够理解在同时查询同一端点时如何处理多个用户。) - 我已经使用Apache JMeter来测试应用程序在同时进行的POST调用上的性能,似乎能够在约170毫秒内处理它们。(我将在下面发布结果的截图。)
代码
我将尝试包含演示负责调用Geocoding API的控制器的代码。
@RestController
@RequestMapping("/api")
@Slf4j
@RequiredArgsConstructor
@Component
public class GeocodingController {
private final OkHttpClient httpClient = new OkHttpClient();
@PostMapping(value = "/reversegeocoding")
public String getReverseGeocode(@RequestBody LatLng latlng) throws IOException, ExecutionException, InterruptedException {
String encodedLatLng = latlng.toString();
Request request = new Request.Builder()
.url("https://maps.googleapis.com/maps/api/geocode/json?" +
"language=en&result_type=street_address&latlng=" + encodedLatLng +
"&key=MY_API_KEY")
.build();
CallbackFuture future = new CallbackFuture();
httpClient.newCall(request).enqueue(future);
Response response = future.get();
return response.body().string();
}
}
getReverseGeocode()
方法接受以下对象作为参数:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LatLng {
double lat;
double lng;
public String toString() {
return lat + "," + lng;
}
}
所以,一旦请求到达,Request Body
就会映射到上述对象上。
最后,CallbackFuture
只是一个基于这个答案的适配器类。
public class CallbackFuture extends CompletableFuture<Response> implements Callback {
@Override
public void onFailure(Call call, IOException e) {
super.completeExceptionally(e);
}
@Override
public void onResponse(Call call, Response response) {
super.complete(response);
}
}
JMeter 结果
英文:
This question might be considered opinionated but I really can't seem to find a straight answer. So either I'm missing something or I'm asking the wrong questions.
So, I'm an undergrad student and new in the whole Spring app development and I'm currently creating an app with React acting as the frontend and building a RESTful API using Spring in order to support it with necessary operations for the backend. Among other services, the backend API I'm building is used as a middle-man forwarding calls to the Google Geocoding API and other 3rd party APIs. **My biggest question since I started this is, is this a valid strategy? ** (at least, something acceptable by industry professionals)
My Research
- The biggest pro that this method has is that it allows me to effectively hide my API keys from the client rendering any attacks impossible.
- At the same time, this whole process adds (I believe) unnecessary complexity and delay in the overall responses resulting in a possible hindered user experience.
- Something I'm not quite sure yet would be that I'll have to add
async
capabilities to the services exposed by my own API in order to facilitate multiple users. (This last part might be entirely wrong but I haven't been able to understand how multiple users are handled when querying the same endpoint concurrently.) - I have used Apache JMeter to test the performance of the app on concurrent POST calls and it seems to be able to handle them on around 170ms. (I'll post screenshots of the results below.)
Code
I'll try to include code demonstrating the controller that's responsible for the calls to the Geocoding API.
@RestController
@RequestMapping("/api")
@Slf4j
@RequiredArgsConstructor
@Component
public class GeocodingController {
private final OkHttpClient httpClient = new OkHttpClient();
@PostMapping(value = "/reversegeocoding")
public String getReverseGeocode(@RequestBody LatLng latlng) throws IOException, ExecutionException, InterruptedException {
String encodedLatLng = latlng.toString();
Request request = new Request.Builder()
.url("https://maps.googleapis.com/maps/api/geocode/json?" +
"language=en&result_type=street_address&latlng=" + encodedLatLng +
"&key=MY_API_KEY")
.build();
CallbackFuture future = new CallbackFuture();
httpClient.newCall(request).enqueue(future);
Response response = future.get();
return response.body().string();
}
}
The getReverseGeocode()
method takes in as an argument the following object:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LatLng {
double lat;
double lng;
public String toString() {
return lat + "," + lng;
}
}
So, the Request Body
is mapped onto the above object once the request arrives.
Finally the CallbackFuture
is just an adapter class based on this answer.
public class CallbackFuture extends CompletableFuture<Response> implements Callback {
@Override
public void onFailure(Call call, IOException e) {
super.completeExceptionally(e);
}
@Override
public void onResponse(Call call, Response response) {
super.complete(response);
}
}
JMeter Results
答案1
得分: 4
是的,这是经常发生的事情。并非所有外部API都设置为允许您直接授予用户访问权限。您可能还需要进行日志记录和/或访问控制。这意味着您需要为这些调用分配资源,但除非您期望负载过大,否则不值得过早进行优化。有时,您可以将代理责任卸载到像Nginx之类的东西上,这可能比您的应用程序后端更高效。
根据我的经验,将这些代理保持在与您的其他代码隔离的单独包中是值得的。然后,如果需要独立扩展它们而不影响主应用程序,您可以轻松拆分它们出来。
英文:
Yeah, this is done all the time. Not all external APIs are setup to allow for you to grant access to your users directly. You also may have requirements for logging and or access control. It does mean you need to dedicate your resources to the calls but unless you are expecting an excessive load its not worth optimizing for too far in advance. Sometimes you can offload the proxy responsibilities to something like nginix instead which can be more efficient than your application backend.
In my experience it is worthwhile to keep these proxies in their own separate package that is isolated from your other code where possible. Then if you need to scale them independently of your main app you can easily break them out.
答案2
得分: 3
这是一个完全有效的策略。使用这种方法有许多好处,虽然它可能会感觉增加了不必要的复杂性,但通常好处远远超过了成本。
首先,你避免了“泄漏抽象”,客户端不应关心你如何实现特定功能,他们只关心它是否正常工作!这还意味着你应该能够在将来更改实现而不让客户端知道。
其次,你将你的API与其他API解耦。如果封装的API发生变化,你可以自己处理,而不需要客户端更改其代码(有时可能无法做到,但这提供了很好的防御措施)。
此外,它为你提供了一个方便的实现自己功能的点,围绕这些API(例如速率限制、访问控制和日志记录)。
@Async
问题不仅适用于封装第三方API,它也适用于所有具有阻塞IO的终点。
英文:
Yes, this is an entirely valid strategy. There are many benefits to using this approach and whilst it can feel like it's adding unnecessary complexity, the benefits often out way the cost.
Firstly you're avoiding "leaky abstractions", the client shouldn't care how you implement a particular piece of functionality, they just care that it works! It also means that you should be able to change the implementation in the future without the client even knowing.
Secondly, you're decoupling your APIs from others. If the wrapped API changes, you can handle this yourself without the clients having to change their code (there will be times when this can't be done, but it offers a good defence to this).
Also, it gives you a convenient point to implement your own functionality around these APIs (rate limiting, access control and logging for example).
The @Async
issue is not specific to wrapping third party APIs, it's an issue for all endpoints you have which have blocking IO.
答案3
得分: 2
这是普遍的。最近对第三方 API 进行了操作,添加了跨域头,以便客户端可以执行跨域请求。此外,原始的 JSON 在某些情况下格式不正确,因此可以干净地处理该情况。
第一个非常容易使用 Spring Boot,您只需使用 @CrossOrigin(maxAge = 3600)
装饰控制器即可。
英文:
It is common. Recently did this to a third party api adding Cross Origin headers so clients could perform cross origin requests. Also the original JSON was malformed in some cases so it was possible to cleanly handle that scenario.
The first is very easy with Spring boot and as you can just decorate the controller with @CrossOrigin(maxAge = 3600)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论