Creating rest endpoints for consumer and producers - java

I am trying to write rest endpoints for user and his services. Not sure for the below use case it should be in User or Service controller.
The below examples will find the service in which the "userId" is a consumer or producer
http://localhost/user/{userId}/consumer/services
http://localhost/user/{userId}/producer/services
or
http://localhost/service/consumer/user/{userId}
http://localhost/service/producer/user/{userId}
The first example makes more sense to me, any suggestions and a good approach?

If you ask me, I would recommend the 1st approach. But having said that, I would route the requests to the Controllers this way
#RequestMapping(path = "/user")
public class UserController{
// write code for User API
}
#RequestMapping(path = "/user/{userId}/consumer")
public class ConsumerController{
// write code for Services API
}
#RequestMapping(path = "/user/{userId}/producer")
public class ProducerController{
// write code for Services API
}

Neither one. As long as the user is not the subject of action (entity on which action is performed), user info is the metadata which you can (and should) derive from the Authentication principal rather than what's coming on the URL

I am planning to use the following as it seems more appropriate.
http://localhost/service?userId={userId}&userType={userType}
userType= CONSUMER OR PRODUCER
The above will return the services where the userId is a CONSUMER or PRODUCER of the service.

Related

How to write async POST/GET in Java Microprofile correctly

I'm a C developer who needs to create a basic async REST server (synchronous version works fine) in Java using the Microprofile (Helidon MP) framework. Here is my strategy to do this:
The client should do the POST call and provide JSON objects that the POST endpoint will receive. Then, the POST Endpoint method will call a business logic that should do stuff with the received JSON objects. This logic must be run asynchronously. POST should immediately return 202 Accepted.
The client should check for async task completion status using a GET request (simple pooling style).
Should POST return a URI that the GET call will use? How? This GET should also provide the percentage of the task completion if the task is in progress. Finally, if the business logic is done, the GET should return the result.
I have a little previous experience with async Java, but no experience with async in this Microprofile/Java EE/Jakarta or whatever it is. I tried several different approaches (AsyncResponse, CompletitionStatus, etc.) to write this code (async POST Method) but nothing seems to be working. The skeleton of the POST functions looks like this:
#Path("/logic")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response logic(JsonObject jsonObject){
BusinessLogic bl = new BusinessLogic();
// This should be done asynchronously.
//How to do it correctly in Microprofile?
bl.doSomethingWithInput(jsonObject);
return Response.status(Response.Status.ACCEPTED).build(); //Return ACCEPTED and the task URI???
}
The GET Handler:
#Path("/logicstatus")
#GET
##Produces(MediaType.APPLICATION_JSON)
public Response logicStatus(URI as query param???) {
// Magic here?
retrun Response.status(Response.Status.OK).entity(resultJson).build();
}
Thanks a lot. Java reminds me how I love writing device drivers in C :D.
So, first things first. To run things asynchronously Jakarta EE has the annotation #Asynchronous. So, create a CDI bean with #RequestScoped, and put there your business method annotated with #Asynchronous.
Add #ApplicationScoped CDI annotation to your JAXRS service so you can inject ypur business bean.
Then, for monitoring the task, you have a couple of possibilities.
If you keep the state in a database you just make the #GET method to check the state in the database. But I asume this is not your case.
If the first approach doesn't fit then you have to keep the state of the different inProgress tasks somehow. I would have another #ApplicationScoped CDI bean for that. This would contain a ConcurrentHashMap, with a UUID as the key, and as the value an Object of your own that contains the current state of any specific async job.
The UUID has to be generated in the #POST method, and sent as a parameter to the #Asynchronous, method. This way the #POST method can return the UUID, which will be used then in the #GET to request the state of the task (querying the #ApplicationScoped bean).
So the application-wide async tasks state holder should be something like this
#ApplocationScoped
public class AsyncTasksStateHolder {
private Map<UUID, MyCustomState> tasksState = new ConcurrentHashMap<>();
// here go methods to add and get from the map
}
Then, the async business logic may be something like:
#RequestScoped
public class AsyncTaskExecutor {
#Inject
AsyncTasksStateHolder asyncTasksStateHolder;
#Asynchronous
public void doAsyncStuff(UUID uuid, JsonObject jsonObject) {
// create the initial state and save it to asyncTasksStateHolder
// Do your dirty deeds
// Keep updatinf the state in asyncTasksStateHolder
}
}
And now the JAX-RS part:
#Inject
AsyncTasksStateHolder asyncTasksStateHolder
#Inject
AsyncTaskExecutor asyncTasksExecutor;
#Path("/logic")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public Response logic(JsonObject jsonObject){
UUID uuid = UUID.randomUUID();
asyncTasksExecutor.doAsyncStuff(uuid, jsonObiect); // this returns immediately because of the #Asynchronous
return Response
.status(Response.Status.OK)
.entity(uuid.toString())
.build();
}
#Path("/logicstatus/{uuid}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response logicStatus(#PathParam("uuid") String uuidAsString) {
// And here you just query the asyncTasksStateHolder
// with the UUID to retreive the state.
}
Some thing to note here:
You will need a mechanism to remove stale state, or the map will grow indefinately.
I used PLAIN_TEXT for simplocity, vut feel freento model the #POST response as you please.
Depending on your MicroProfile version you will nees to create the beans.xml so CDI gets activated.
And I think that's all. Any problems or doubts do not hesitate to write a comment.

Best way to use Websocket with Spring Boot and Vuejs

I try to use Websocket with spring boot backend (as an API) and Vuejs frontend.
I take a simple use case to expose my question. Some users are logged on my website, and there is a messaging feature. User A send a message to User B. User B is actually logged, and I want to notify User B that a new message is arrived.
I see 3 ways to do it with websockets :
1 - When User A send message, an Axios post is call to the API for saving message, and, if the Axios response is success, I call something like
this.stompClient.send("/app/foo", JSON.stringify(bar), {})
2 - When User A send message, I only call something like
this.stompClient.send("/app/foo", JSON.stringify(bar), {})
and it's my controller's method (annotated with #MessageMapping("/xxxx") #SendTo("/topic/yyyy")) that call facade, service, dao to first, save message, then return message to subscribers
3 - I keep my actuals controllers, facade, services and DAO, and juste add when save is successfull something like :
#Autowired SimpMessagingTemplate webSocket;
...
#GetMapping("/send-message")
public ResponseEntity sendMessage(#AuthenticationPrincipal User user, ....) {
service.saveMessage(....);
webSocket.convertAndSend("/ws/message-from", message);
without a new controller contains #MessageMapping("/xxxx") #SendTo("/topic/yyyy"). User B is just subscibed to "/ws/message-from"
Could you help me.
In the 3 way there is a good method ?
Thanks you.
The one and two method has no much difference as you use axios from npm for sending request and the other one you can directly,while the third one you use controller,and facade dao at single place.it is about architecture and how you wanna send your requests for your framework,as a requirement.
They serve best at their level,till you come with specific requirement.
The suggestion would be to use axios.
It has advantages:
supports older browsers (Fetch needs a polyfill)
has a way to abort a request
has a way to set a response timeout
has built-in CSRF protection
supports upload progress
performs automatic JSON data transformation
works in Node.js

HTTP Post request handler in java

I hope someone will be able to help me understand how to create an endpoint HTTP server listener. I'm trying to create a POST request handler that can save all post requests made to a text file.
The purpose is for a Game state integration between My application and Counter-Strike. Ive read their documentation (csgo GSI documentation) and the example given in here is almost exactly what I'm looking for. But its written in nodejs and I will need it to work with Java.
I have only been able to create a HTTPServer but can't seem to understand how I can create a POST request handler which records the data sent to "data" request.
How can I create a handler which can record all requests sent to data?
I believe the easiest & fastest way is to grab a SpringBoot app from https://start.spring.io/ (add Web dependency). And then create a Spring #RestController like that:
#RestController
#RequestMapping(value = "/cs")
public class CsController {
#RequestMapping(value = "", method = RequestMethod.POST)
public void processCsData(#RequestBody CsData csData) {
processCsData(csData);
}
}
where CsData is a POJO class that they send to you. processCsData() is your method to do whatever you like with the data.
Now you need to host it somewhere so that it would be reachable from the Internet or you can use https://ngrok.com/ to create a tunnel for test purposes.

Data validation across different microservices

I've already read lots of topics about it, but still haven't found the better approach.
I have a User. One User may have many Posts. Users and Posts are different microservices. I'm using Spring Boot.
When the front-end call my Posts microservice sending a POST request to /posts/user/1, I need to check if the given userId (1) exists on my Users database. If no, throw an exception telling the front-end that the user doesn't exist. If yes, then insert the given request body as a Post.
The question is: how should I check this information at my backend? We don't want to let this responsibility with the front-end, since javascript is client-side and a malicious user could bypass this check.
Options:
REST communication between the microservices. (Posts microservice call Users microservice asking if the given id exists on his side)
Give Posts microservice access to Users microservice's database
I understand that communication between them will create coupling, but I'm not sure if giving Posts access to Users database is the best option.
Feel free to suggest any options.
You have an option to do interprocess communication between Post and User microservices through RESTful approach.
In case if you just want to check the existence of the resource and don't want any body in response then you should perfer using HEAD http method. Therefore your API endpoint hosted at User microservice will look like -
HEAD user/{userId}
Call this API from Post microservice.
Return 200 / OK if user exist
Return 404 / Not Found if user does not exist
Click here and here to get more details on HEAD method usage and use cases.
For this very particular use case, if you have a security layer, you can(should) make use of user access token, to ensure, that request is processed for the right user, which can be done by validating the token and relying on the fact that if user has token he exist. (As its just not about if user exist)
For any logic other than that, say you want to check if he is allowed to post or other such restrictions it is required to make a call to the user service.
Talking about giving access to the database, it will be against one basic guideline of microservices. Doing so will form a tight coupling between you and user. Its ok to call user service in this case which can decide how to serve this request.
User service on its part should provide ways to answer your queries within the SLA by caching or other mechanisms.
One more thing that you can explore is BFF (Backend for Frontend)
You rightly said you should not expose backend services to frontend or add any logic there, but often frontend pages may not be comfortable in accepting that content on same page is answered via n different back end services and there might be some logic to stitch such queries and thats where you can make use of BFF.
Backend server (in my case node) which take of things like these requiring frontend to make just one call(or less calls) for a given page and at the same time hiding your backend services within.
You're right, you must do a validation at the back end since, I suppose, it's a REST service and requests can be send not only from the UI.
Suppose you have a service implementation:
#Service
class UsersServiceImpl implements UsersService {
private final Users users;
public UsersServiceImpl(Users users) {
this.users = users;
}
#Override
public void addPost(long userId, Post post) {
User user = users.get(userId);
if (user == null) {
throw new UserNonExistent(userId);
}
user.addPost(post);
}
}
where Users is an interface representing a users database and UserNonExistent is a RuntimeException. Then in your controller you can do the following:
#RestController
class UsersController {
private final UsersService usersService;
public UsersController(UsersService usersService) {
this.usersService = usersService;
}
#PostMapping("/posts/user/{userId}")
public void addPostToUser(#PathVariable String userId, #RequestBody Post post) {
usersService.addPost(userId, post);
}
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "User does not exist")
#ExceptionHandler({UsersService.UserNonExistent.class})
public void handleUserNonExistentException() {
// Nothing to do
}
}
If the supplied user ID is invalid handleUserNonExistentException() method will be invoked and it will return a BAD REQUEST HTTP status code.

Spring REST client - force response before method finishes

I'm writing a REST service with Spring Web 4.0.5 and one of called methods is sending e-mail (with javax mail). Sending mail takes some time, but I would like to be able to send HTTP response (no matter what response, e.g. 200) BEFORE this method finishes - so before the mail is sent. Is it even possible? Preferably without multithreading?
#RestController
#RequestMapping(value = "/mails", produces = "application/json")
public class RestMailService{
#Autowired
MailService mailService;
#RequestMapping(value="/test", method = RequestMethod.GET)
public void sendMail(){
mailService.sendMail();
}
}
I believe all possible solutions include multithreading. The thread will be either directly started by you or hidden behind messaging or something similar.
if you were to go with multi-threading after all please use some Executor instead of below suggested new Thread(...).start()
I would also note that returning HTTP 200 before the operation finishes may somewhat confuse the user as the code suggests the operation was successful where in fact the operation maybe didn't even start yet.

Categories

Resources