Spring Boot - how make different resources for different users - spring-boot

I am writing a spring boot application and have encountered the problem of sharing resources for different users. Simplified by example, it looks like this: there is one variable. You can assign a value to it through the form on the page. If the first user assigns the value hello java from one browser, then the second user will see the same value through another browser. I dont know how to make each user work with their own variable and their values do not overlap?
Controller:
#Controller
public class MessageController {
private String message;
#GetMapping(value = "/show_message")
public String showMessage(Model model){
model.addAttribute("message", message);
return "message";
}
#PostMapping(value = "set_message")
public String setMessage(#RequestParam(name = "newMessage") String newMessage){
message = newMessage;
return "redirect:/show_message";
}
}
html page:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Message value: <th:block th:utext="${message}"/></p>
<p>Enter a new message value</p>
<form method="POST" th:action="#{/set_message}">
<input type="text" name="newMessage"/>
<input type="submit" value="Send"/>
</form>
</body>
</html>

The rule of thumb is that controllers as well as services are not designed to maintain a state, particularly the client state.
Besides, Spring beans are by default singletons : they are shared among clients/requests. That explains the behavior that you notice.
To solve that issue you may use the in-memory HttpSession (old practice) but that is not advised any longer because it sticks the client/user to a specific server instance that has first served his request.
You should preferably either resend the data at each request (which may be cumbersome to do) or better store the data in an in-memory database such as Redis.
That is fast and it doesn't stick the client to a specific instance of your spring boot application.
With Spring, a good association with the in-memory database usage to handle the user state is using HttpSession but Spring Session backed to a database (Redis or another).
For how to use the Session with Spring MVC, you have that good post.

Related

Spring Boot HTML page not rendering

I am in the process of learning Spring Boot and became unstuck when trying to post model data to an HTML file.
I have a controller, where I populate the model and call an HTML page from. When I put a breakpoint inside this method, the model data gets populated correctly, but the HTML page is rendered with only the name of the HTML file, and nothing else (no browser errors either). I am thinking it may have something to the with the file structure, and the fact that my RestController already has a path specified (because when I create a clean new controller with no explicit class-based #RequestMapping specified and call the template from the root path + name of HTML file, it renders correctly). I do have the ThymeLeaf dependency installed, and "userView.html" is placed inside the "template" directory.
ReaderController.java extract:
#RequestMapping("/reader")
public class ReaderController {
...
#RequestMapping(value = "/userView")
public String getUser(Model model) {
// business logic goes here
model.addAttribute("userName","Somebody");
model.addAttribute("url", "www.example.com");
return "userView";
}
userView.html extract:
<body>
<h1>User Data</h1>
<p th:text="'Username: ' + ${userName}"/>
<p th:text="'Url: ' + ${url}"/>
</body>
http://localhost:8080/reader/userView only renders the word "userView".
I found the solution to the issue. I inadvertently used the #RestController annotation instead of the #Controller annotation to the controller class. This link helps to explain the issue.

ThymeLeaf: Cannot display view after calling endpoint

I am trying to call a REST endpoint and then display a ThymeLeaf template:
The Endpoint:
#GetMapping("/devices")
public String getDeviceDetailU(Model model) {
List<FinalDevice> devices = deviceService.getAll();
model.addAttribute("devices", devices);
return "deviceList";
}
For the endpoint I tried returning /deviceList, /deviceList.html, deviceList.html.
Whenever I navigate to the endpoint, I simply get the string that was returned.
Here is the template:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<body>
Hello World!
</body>
</html>
While I understand, at this point, it will not display the list, I just want to be forwarded to the template.
If I go to localhost:8080/deviceType I display that template. This to me indicates it is not a security or configuration issue.
Any ideas?
This should all work according to this tutorial.
You probably have #RestController instead of just a #Controller.
If you want templates to be rendered you need to use #Controller. #RestController means that all your #Mappings simply serialize the return value and output it as json or xml (which is why you are seeing the string deviceList instead of the template).

Spring boot + thymeleaf: get logged in user

I would like to know, how I can get the User object from Thymeleaf. Currently I am calling my userService, which will get the user from the DB. I don't like this approach, because for every call a db query will be made.
Is it possible to get the user from memory?
<link href="/css/style2.css"
th:if="${#commanderService.getCurrentCommander()} and
${#commanderService.getCurrentCommander().settings == 'template=1'}"
rel="stylesheet" type="text/css" />
CommanderService:
public Commander getCurrentCommander() {
Object principal =
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Commander commander = findByName((String)principal);
return commander;
}
If you are using spring security and thymeleaf, you could check:
https://github.com/thymeleaf/thymeleaf-extras-springsecurity3
For example:
<div sec:authentication="name">
The value of the "name" property of the authentication object should appear here.
</div>
<div sec:authorize="hasRole('ROLE_ADMIN')">
This content is only shown to administrators.
</div>

How to Return a View from a Controller to an iFrame

I am new to MVC 3 and have come accross the following scenario:
First let me explain how I have setup my application:
All post backs to the server use jquery ajax which which return a view from the controller that either appended or prepended or replace a targeted div.
The Scenario:
I have come to a point where I would like to upload images but unfortunately because of the jquery ajax posting I cannot get the values for an html in C# Request.Files. I know there are plugins out there to help out with this but I would like to do this myself so I have created an <iframe> which i then use a bit of javascript and post the form targeted to the iframe (old classic way of doing things):
function UploadImg(SACTION) {
alert(SACTION);
validatorform.action = SACTION;
validatorform.target = "fraImage";
validatorform.method = "POST";
validatorform.submit();
}
the SACTION parameter looks like this #Url.Action("UploadFile", "Response"). This all works well as it hits the controllers action method and I can then save the image:
[HttpPost]
public ActionResult UploadFile(string ArticleID)
{
ViewBag.PreviewImage = cFileUploads.UploadFile(ArticleID, Request.Files[0]);
return View("ImagePreview");
}
I would now like to return a view to the iframe (simply to preview the image and then do a couple of other things but this is besides the point)
The View for previewing the Image:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="">
<img alt="" src="#ViewBag.PreviewImage" />
</form>
</body>
</html>
The Problem:
Unfortunately when I return the View (ImagePreview.cshtml) in C# the whole page is refreshed, all I want is for the iFrame to be refreshed. How should I return the view from the controller?
Fiqured out the problem, I had a bit of javascript that was replacing the contents of a div that the iframe was sitting in ... (slap on my forehead). All working perfectly now :)
Will leave this question here just incase ..

grails IE ajax problem

I'm using grails to develop my application and the 'prototype' library to do an AJAX call with which
I'm having a problem with on IE. In all other browsers my application works fine.
Here is my code:
<html>
<g:form action="ajaxcall" id="recform">
<g:select id="aseselect" name="art" from="${dropdownList}" optionKey="id" optionValue="value" noSelection="['':'- Select -']"/>
<g:submitToRemote action="ajaxcall" value="submit" update="updatediv" />
</g:form>
<div id="updatediv"></div>
</div>
</html>
And this is my controller code:
def ajaxcall = {
String toRender="";
//code that makes db call and adds html into the toRender string
render toRender;
}
The 'toRender' string contains html of an unordered list which renders fine in firefox, chrome and safari but not IE which seems to not get the whole list sometimes or get an empty list some of the times. The behaviour is totally unpredictable depending on IEs mood.
Has anyone come across this issue before? How can I solve this?
Thanks
It's due to IE's caching. I add
response.setHeader("Cache-Control", "no-store")
to controller methods for ajax calls, which tells the browser not to cache that response.
So your controller method should look something like:
def ajaxcall = {
response.setHeader("Cache-Control", "no-store")
String toRender="";
//code that makes db call and adds html into the toRender string
render toRender;
}
There's a more detailed explanation here:
Grails: best way to send cache headers with every ajax call
I fixed the IE (11) browser AJAX using FormData() post to Grails controller problem by removing
< meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
or change it to
< meta http-equiv="X-UA-Compatible" content="IE=Edge" />
in the HTML <head> section.

Resources