add view counter for GET REQUEST - java

Im here again to ask help about a little ( i hope is little) problem.
What my friend told me to do, is to add a view/click counter in the GET request and save it into a DATABASE (actualy working with DBEAVER).
I m still looking for a way to do it but i have no idea. can you help pls ? Here the code.
oh btw im using springtools
this is the controller:
#Controller
#RequestMapping("/")
public class RecipeController {
//ADMIN- USER
#GetMapping("/user")
public String user() {
return "user";
}
#GetMapping("/admin")
public String admin() {
return "admin";
}
and this is the model
private int hitCount;
public int getHitCount() {
return hitCount;
}
public void setHitCount(int hitCount) {
this.hitCount = hitCount;
}
i hope is enough clear, is more info is need im here :D
Thx alot

2 options come to my mind :
Create another service-class, which will have a method to track (and update) the API-call count, (you can make DB calls in that method).
Use annotations(AOP) to make a generic handler for tracking the API call counts. Try searching for #Before it comes from org.aspectj.lang.annotation package.

Related

Creating like/dislike system on java/spring

Like/dislike system.
App has entity Post. Post has field List likes and it joins table, which has columns post_id, user_id.
When User presses button "like" app will add authenticated user in List in PostService. But I need to have the "isLiked" boolean field. This will define what the Like button will look like in frontend.
I can get value for field countLike just call method size() from field likes.
But I don't know now I can get value for field "isLiked".
Help me with it, please.
#Entity
public class Post {
//some fields...
//there I saved users, who has posed "like"
private List<User> likes;
#Transient
private int countLike;
//there I want to save status - liked/disliked;
#Transient
private boolean isLiked;
}
#Service
public class PostService {
//some fields and methods...
public void createLike(int postId, User authenticatedUser) {
Post post = postRepository.getOne(postId);
post.getLikes().add(authenticatedUser);
this.update(post);
}
}
While the approach hinted to by yourself and the comment by "Lino - Vote don't say Thanks" will work, I don't think they are a very good idea.
Working but wastful.
You can create methods like the following:
public int likeCount{}{
return getLikes().size();
}
public boolean isLiked(){
return getLikes().size() > 0;
}
The problem with that is, it will load a lot of data just for providing a single number of even just a single bit of information.
More efficient in most scenarios
Instead I recommend loading the information from the database.
Assuming you are using Hibernate as the JPA implementation you can do that with the #Formula annotation. With this the relevant code looks like this:
#Entity
public class Post {
#Formula("(select count(user_id) from Likes l where l.post_id = post_id)")
private int countLike;
public boolean isLiked(){
return getCountLike() > 0;
}
}

How to get the ID of inserted object when using AsyncTask?

I had to create an account to ask this question because I couldn't find the right way to do this. The only thing that comes close is this question here, but it doesn't go all the way and I'm still stuck. Here we go...
I'm trying to build an app following as much of the Architecture Components principles.
I'm currently trying to add a row in one of my database table, and get the ID of this row in return, to then insert a row in another table, with a reference to the first one.
I've created my database object:
#Entity(indices = {#Index("id")})
public class Search {
#PrimaryKey(autoGenerate = true) private int id;
...
And the corresponding DAO:
#Dao
public interface SearchDao {
#Insert
long insert(Search search);
...
As you can see, my DAO returns a long with the created ID. This is the behavior which was pointed out in the question I linked before, and documented here.
Since I'm following Android Architecture Components principles, I'm using a Repository class to do all my database related work. In this Repository, I've created a public method to insert a new object, which is creating and executing an AsyncTask to do the work:
public class Repository {
public void insertSearch(Search search) {
new insertSearchAsyncTask(this.mSearchDao).execute(search);
}
...
private static class insertSearchAsyncTask extends AsyncTask<Search, Void, Long> {
private SearchDao mAsyncTaskDao;
insertSearchAsyncTask(SearchDao dao) {
this.mAsyncTaskDao = dao;
}
#Override
protected Long doInBackground(final Search... params) {
long id = this.mAsyncTaskDao.insert(params[0]);
return id;
}
}
I know I can use the onPostExecute(long id) method to do stuff with the result of the doInBackground method, but this onPostExecute method cannot return anything to the insertSearch method, where I created the AsyncTask and executed it.
I know need to change the return type of my insertSearch method to long. However if I want to have something to return, I need to get the result of the execution of the AsyncTask. How can I do that?
I've tried this (according to the validated answer):
public class Repository {
private long result_id = 0;
public long insertSearch(Search search) {
new insertSearchAsyncTask(this.mSearchDao).execute(search);
return result_id;
}
private class insertSearchAsyncTask extends AsyncTask<Search, Void, Long> {
private SearchDao mAsyncTaskDao;
insertSearchAsyncTask(SearchDao dao) {
this.mAsyncTaskDao = dao;
}
#Override
protected Long doInBackground(final Search... params) {
long id = this.mAsyncTaskDao.insert(params[0]);
return id;
}
#Override
protected void onPostExecute(Long search_id) {
result_id = search_id;
}
}
}
But this feels very very wrong. I had to make the insertSearchAsyncTask class not-static, and I have to store the result of the insert in an attribute of my Repository.
I'm hoping there is a better/correct way of doing this.
I've also looked at other suggested answers on the link above, especially one about Delegates, but this doesn't suit my need as I need the method insertSearch to return the result, not another one called by the AsyncTask when it finishes.
I hope I've explained my problem clearly enough.
Any idea anyone?
Thanks a lot!!

How to fix Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) in java

I have a Controller class with the below two methods for finding a doctors (context changed). Getting the
Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) error on both methods.
#Controller
#RequestMapping(value = "/findDocSearch")
public class Controller {
#Autowired
private IFindDocService findDocService;
#RequestMapping(value = "/byName", method = RequestMethod.GET)
#ResponseBody
public List<FindDocDTO> findDocByName(FindDocBean bean) {
return findDocService.retrieveDocByName(bean.getName());
}
#RequestMapping(value = "/byLoc", method = RequestMethod.GET)
#ResponseBody
public List<FindDocDTO> findDocByLocation(FindDocBean bean) {
return findDocService.retrieveDocByZipCode(bean.getZipcode(),
bean.getDistance());
}
}
and my Bean is :
public class FindDocBean implements Serializable {
private static final long serialVersionUID = -1212xxxL;
private String name;
private String zipcode;
private int distance;
#Override
public String toString() {
return String.format("FindDocBean[name: %s, zipcode:%s, distance:%s]",
name, zipcode, distance);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
As per all the suggestions found so far, they are suggesting to restrict the bean with required parameters only by something like below :
final String[] DISALLOWED_FIELDS = new String[]{"bean.name", "bean.zipcode", };
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(DISALLOWED_FIELDS);
But my problem is all the 3 parameters of the bean will be used in either of the method supplied on Controller.
Can someone please suggest some solution for this. Thanks in advance.
InitBinder can be used for methods. You can try this.
#InitBinder("findDocByName")
public void initBinderByName(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"distance","zipcode"});
}
#InitBinder("findDocByLocation")
public void initBinderByZipCode(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"distance","name"});
}
i was facing same issue, then i added below code in same rest controller class:
#InitBinder
public void populateCustomerRequest(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{});
}
now its working fine for me and mass assignment issue was fixed.
Simple question - how your mapper can instantionate the bean? Here is answer / example. You can pass that data by query parameter, or in header. However that would be strange. Better is to have that methods with #QueryParam providing location, or name. That way it will be easier to protect your application.
As a side note, query has limited length, so if your search form is big and strange, #POST can be good idea, and that way you can pass all the data. For this, simple example that would be overkill.
This looks like an unfortunate false positive. The rule behind this error is made to avoid that properties present in an object but not intended to be (unvalidated) user input are accidentally populated from a web request. An example would be a POST request creating a resource. If the request handler takes the full resource object and fills only missing properties an malicious user could populate fields that she shouldn't be able to edit.
This case however does not match the scheme. You just use the same mechanism to capture your different arguments. Additionally populated properties will not even be read. In
GET http://yourhost/findDocSearch/byName?name=Abuse&zipCode=11111
the additional zipCode would just be ignored. Therefore the assumed risk is not present here.
To fix the warning, you could mark it as a false positive (if this is possible inside your setup). If that is not possible you could also just map the query parameters to method arguments directly. As you only have limited parameters that should not harm too much. If this is also no option you probably need to figure out the exact algorithm your code analysis uses to figure out what checks it will recognize. Unfortunately most scanners are only able to discover a limited set of ways to do input validation.

Play framework sometimes needs getters and setters, sometimes it does not

I started a simple webapp based on play. After a bit of refactoring the login-form stopped working. I used an entity-bean with simple public fields. I moved it from one controller to another while refactoring and of cause corrected the references. It always told me I'm an invalid user.
During debugging I've found that the fields aren't set anymore. However, what really confused me: I manually added getters and setters to the public fields and suddenly it worked again. I've done now quite a bit of research why it works in the default-controller called "Application" but not in my own one called "Registration".
There isn't much code involved, here a few points:
public class RegistrationLogin extends Controller {
public static class Login {
#Required
public String email;
#Required
public String password;
public String validate() {
/* here is the interesting part, when I call "form.hasErrors" in
authenticateLogin and this validate-method gets called, email and
password both are null. If I create getters and setters they are set correctly */
if (User.authenticate(email, password) == null) {
return "Invalid user or password";
}
return null;
}
}
public static Result authenticateLogin() {
Form<Login> loginForm = form(Login.class).bindFromRequest("email", "password");
String title = "Login";
if (loginForm.hasErrors()) {
return badRequest(login.render(title,loginForm));
} else {
session().clear();
session("email", loginForm.get().email);
return redirect(
routes.Application.show(Ebean.find(User.class).where().ieq("email",loginForm.get().email).findUnique().getName())
);
}
}
When I had Login defined in Application (the default-controller which is generated when you start a project) it worked with just the fields.
Whats the origin of this behavior? Any hint might be helpful.

Java enum: Return value other than String

I can't find an answer to this question anywhere so I'm hoping someone can help me out. I'm expecting that what I am asking is not possible, but I wanted to confirm. First, an enum example...
public enum StatusCode {
SUCCESS(0), GENERAL_ERROR(999), CONNECTION_TIMEOUT_ERROR(1337);
private int statusCode;
private StatusCode(int statusCode) {
this.statusCode = statusCode;
}
public int getStatusCode() {
return statusCode;
}
}
As you can see, I am using this enum to force specific status codes. My question is this: Is there a way that I can reference StatusCode.SUCCESS and have it return the int value associated with it? Rather than get into too much detail about what I would like to do, take this example:
public String getStatusMessage(int statusCode) {
// Map<Integer, String> that contains status messages
// return String for key statusCode
}
In this example, the syntax for calling this method is getStatusMessage(StatusCode.SUCCESS.getStatusCode()).
Is there a way to shorten this to getStatusMessage(StatusCode.SUCCESS)?
I think the second way looks much cleaner and would make my code much more readable. Any ideas? Any help is much appreciated. Thanks in advance!
You mean like this?
public String getStatusMessage(StatusCode code) {
int status = code.getStatusCode();
String message = ...do stuff to get message :)
return message;
}
Luckily for you, EnumMap exists just for that situation.
private static final Map<StatusCode, String> mapMessage =
new EnumMap<>(StatusCode.class);
mapMessage.put(SUCCESS, "Success.");
...
You don't even need the method getStatusMessage, just call map.getMessage(SUCCESS).
However maybe you would be better off adding a String message field within StatusMessage and calling the constructors like SUCCESS(0, "Success") and then adding a getter for the message.

Categories