我编写的应用程序使用Spring Session在Sring MVC级别同时管理多个浏览器会话。但是,我不喜欢我的解决方案(它使用多个重定向),并希望找到改进它的方法。详情如下。
我编写了Spring Boot应用程序,它使用Spring Session 1.3.0,Spring MVC和Thymeleaf模板。我添加了它可能同时管理多个浏览器会话,即使在单独的选项卡中单个浏览器中打开也是如此。我从这里尝试了示例代码:
https://github.com/spring-projects/spring-session/tree/1.3.x/samples/users
它奏效了。但是我觉得除了转到servlet级别并编写大量自定义代码来管理会话之外,还有更好的方法可以做到这一点,就像在上面的链接中所做的那样:
UserAccountsFilter implements javax.servlet.Filter
LogoutServlet extends javax.servlet.http.HttpServlet
LoginServlet extends javax.servlet.http.HttpServlet
这个想法来自阅读的结果:
https://docs.spring.io/spring-session/docs/1.3.2.RELEASE/reference/html5/guides/users.html
我想在Spring MVC级别使用它,这是我到目前为止所做的:
1)SessionConfig:配置内存会话存储和CookieHttpSessionStrategy
@Configuration
@EnableSpringHttpSession
public class SessionConfig
{
@Bean
public CookieHttpSessionStrategy sessionStragegy()
{
return new CookieHttpSessionStrategy();
}
@Bean
public MapSessionRepository sessionRepository()
{
return new MapSessionRepository();
}
}
2)当用户打开链接http://mysite/employee.htm/startEdit?id=0时,将创建新员工,否则,如果ID为> 0然后加载现有员工进行编辑。
EmployeeEditStartController的目的是在多会话模式下启动新会话并加载到employee中,以便EmployeeEditController将其用作模型对象。
@Controller
@RequestMapping(EmployeeEditController.URL)
public class EmployeeEditStartController extends AbstractController
{
public static final String LOAD_USER = "loadUser";
@Autowired EmployeeEditService employeeEditService;
@Autowired CookieHttpSessionStrategy sessionManager;
private String getFreeAliasUrl(HttpServletRequest request)
{
String addAlias = sessionManager.getNewSessionAlias(request);
return sessionManager.encodeURL("", addAlias);
}
//startEdit used only for creating new session if there is no one, for example if cookies does not exist or if they are deleted
@GetMapping("startEdit")
public String startNewSession(@RequestParam(value = ID_PARAMETER, required = true) Long id, HttpServletRequest request)
{
request.getSession();
return REDIRECT_PREFIX + "startEdit1" + "?" + ID_PARAMETER + "=" + id;
}
//startEdit1 should be run after new session is created
@GetMapping("startEdit1")
public String startNewSession1(@RequestParam(value = ID_PARAMETER, required = true) Long id, HttpServletRequest request)
{
return REDIRECT_PREFIX + LOAD_USER + getFreeAliasUrl(request) + "&" + ID_PARAMETER + "=" + id;
}
//load user into EmployeeForm and add it to session
@GetMapping(LOAD_USER)
public String addCommandToSession(@RequestParam(value = ID_PARAMETER, required = true) Long id, HttpSession session)
{
session.setAttribute(EmployeeEditController.COMMAND, employeeEditService.getEmployeeForm(id));
return REDIRECT_PREFIX;
}
}
3)EmployeeEditController是添加/编辑员工主屏幕的控制器:
@Controller
@SessionAttributes(EmployeeEditController.COMMAND)
@RequestMapping(EmployeeEditController.URL)
public class EmployeeEditController extends AbstractController
{
static final String COMMAND = "employeeForm";
public static final String URL = "employee.htm";
public static final String REDIRECT_URL = REDIRECT_PREFIX + URL;
static final String VIEW = "employee/addEditEmployee/menu";
@GetMapping
public String get()
{
return VIEW;
}
@PostMapping(params = DISPATCH_PARAMETER + "=save")
public String saveEmployee(@Valid @ModelAttribute(COMMAND) EmployeeForm command, BindingResult bindingResult, SessionStatus status, HttpSession session)
{
//here we saving employee
//each new session will have new id, old session will be destroyed completely and removed
status.setComplete();
session.invalidate();
return REDIRECT_PREFIX + "/" + EmployeeViewController.MAIN_MENU_URL + "?_s=0";
}
}
4)AbstractController只包含一些控制器使用的常用常量:
public class AbstractController
{
public static final String DISPATCH_PARAMETER = "dispatch";
public static final String ID_PARAMETER = "id";
public static final String INDEX_PARAMETER = "index";
public static final String REDIRECT_PREFIX = "redirect:";
}
5)我有单独的控制器用于员工姓名更改,设置部门等。我提供了其中一个(NameController)的示例,因为它显示了我如何管理其中的会话:
@Controller
@SessionAttributes({NameController.COMMAND, EmployeeEditController.COMMAND})
@RequestMapping(NameController.URL)
public class NameController extends AbstractController
{
static final String VIEW = "employee/addEditEmployee/name";
static final String COMMAND = "employeeName";
static final String URL = "name";
@Autowired private EmployeeEditService employeeEditService;
@GetMapping
public String get(ModelMap model, @ModelAttribute(EmployeeEditController.COMMAND) EmployeeForm employeeForm)
{
model.addAttribute(COMMAND, employeeEditService.getPersonName(employeeForm.getEmployee()));
return VIEW;
}
@PostMapping(params = DISPATCH_PARAMETER + "=ok")
public String ok(@Valid @ModelAttribute(COMMAND) PersonName command, BindingResult bindingResult, @ModelAttribute(EmployeeEditController.COMMAND) EmployeeForm employeeForm)
{
//saving name here and then redirect
return EmployeeEditController.REDIRECT_URL;
}
@PostMapping(params = DISPATCH_PARAMETER + "=cancel")
public String cancel()
{
return EmployeeEditController.REDIRECT_URL;
}
}
我的方法有效,但在EmployeeEditStartController中,我有3个控制器方法,通过重定向在链中调用:startNewSession
,startNewSession1
和addCommandToSession
。最后的重定向加载EmployeeEditController#get
。感觉就像是溃疡解决方案。
最后我的问题是:是否有可能摆脱方法startNewSession1
,addCommandToSession
并使startNewSession
方法中的所有工作都没有重定向(1重定向实际上将是:来自startNewSession
到EmployeeEditController#get
)并使其在Spring MVC级别上工作(不在servlet级别上)?或者理想情况下在EmployeeEditController#get
中拥有所有必要的代码,而不需要任何重定向。