I'm using Spring mvc, and wanna make web app which send some json data to client and client should visualized them using js.
I have some questions:
1-My project have some *.html beside *.jsp file how can I handle both without web.xml. the code that i had written work fine with *.jsp but give "The requested resource is not available." error for mapping html files.
2-As i said My service have to send a list of object in json form to client, when i want to parse the json string on server in *.jsp file with the code like
MyClass data = new Gson().fromJson(MyList.get(0).toString(), listType)
,I face with to many problems, so i decide to do this job in client Side in *.html file, i want to know how should i handle this parsing job when i don't want that client know about the structure my class? OR plz tell if have to share my class with client, how should i send stucture of it to html file?
3-How should i access to data that i created in restapi.jsp on the UserSideApp.html file?
these are some my files:
AppInitializer.java
public class AppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(container);
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
} }
AppConfig.java
public class AppConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix("");
return viewResolver;
} }
the part of my AppContoller.java
#Controller
#RequestMapping("/")
public class AppController {
#Autowired
HackDataService service;
#RequestMapping(value = "/restapi", method = RequestMethod.GET)
public String jsonAPI(ModelMap model) {
List<HackData> newList = service.findAllNewData();
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json="";
try {
json = ow.writeValueAsString(newList);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
model.addAttribute("List", json);
String newjson = new Gson().toJson(newList);
model.addAttribute("newList", newjson);
return "newTest.jsp";
}
#RequestMapping(value = "/app", method = RequestMethod.GET)
public String htmlapp() {
return "UserSideApp.html";
} }
and my both .html and .jsp files are in "/WEB-INF/views/"
After spending almost a day on first problem, i find the answer of it,
first of all instead of implementing WebApplicationInitializer, I extend AbstractAnnotationConfigDispatcherServletInitializer and for java Configing i extends WebMvcConfigurerAdapter.
Then i create a pages folder in WEB-INF and my codes changes to these:
AppInitializer became like other internet's samples.
AppConfig.java
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.attackmap" })
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/WEB-INF/pages/");
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix("");
return viewResolver;
}
}
AppController.java
#Controller
#RequestMapping("/")
public class AppController {
#RequestMapping(value = { "/", "/info" }, method = RequestMethod.GET)
public String welcome(ModelMap model) {
model.addAttribute("message", "home page with info about site");
return "homepage.jsp";
}
#RequestMapping(value = "/finalapp", method = RequestMethod.GET)
public String test() {
return "redirect:/pages/final.htm";
} }
there was another way
Creating a "resources" directory in "src/main/webapp"
add bellow cod in AppConfig.java
registry.addResourceHandler("/WEB-INF/views/resources/*.html").addResourceLocations("/resources/");
and for viewResolving use the code like bellow:
#RequestMapping(value = "/newapp", method = RequestMethod.GET)
public String test2() {
return "/resources/UserSideAppNew.html";
}
but still my two other questions are unsolved, the main problem was first one but if you know sth, about these two plz tell me about them
Related
A springboot web project,work by idea . in the controller code,I want to click "manage/operate/createCluster" ,then the idea will open the /web-inf/jsp/manage/operate/createCluster.jsp file.
How to achieve or what information can be referenced?
#RequestMapping(value = "createCluster", method = RequestMethod.GET)
public ModelAndView doClusterList(HttpServletRequest request,
HttpServletResponse response, Model model) {
model.addAttribute("activeMenuId","createCluster");
return new ModelAndView("manage/operate/createCluster");
}
If you want just affiche a view without any data to populate you can add a view controller in Your WebMvc config class like :
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfig implement WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/manage/operate/createCluster").setViewName("manage/operate/createCluster");
}
}
If you want to send data in your view as your example you must just send the view name as String:
#RequestMapping(value = "createCluster", method = RequestMethod.GET)
public String doClusterList(HttpServletRequest request,
HttpServletResponse response, Model model) {
model.addAttribute("activeMenuId","createCluster");
return "manage/operate/createCluster";
}
You should add config of your view Resolver for both cases :
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
I have a Spring Boot app using Thymeleaf 3 as the template engine for the app pages. We use the default configuration provided by spring-boot-starter-thymeleaf, with the HTML Thymeleaf templates under the src/main/resources/templates folder.
Now we would like to use Thymeleaf to generate also some javascript files, using the new javascript template mode. Those javascript templates could be located into the same HTML templates folder or another one (ex: src/main/resources/jstemplates).
I don't know if there is a way to add this configuration without changing anything in the default configuration provided by the spring-boot-starter-thymeleaf, or I have to create a full configuration for everything.
I've tried the first option with the following configuration, which works for the javascript templates, but then html templates don't work anymore.
The configuration:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter
implements ApplicationContextAware {
private static final String UTF8 = "UTF-8";
#Autowired
private SpringTemplateEngine templateEngine;
#Autowired
private ThymeleafProperties properties;
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Bean
public ThymeleafViewResolver javascriptThymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(this.templateEngine);
resolver.setCharacterEncoding(UTF8);
resolver.setContentType("application/javascript");
resolver.setViewNames(new String[] {".js"});
resolver.setCache(this.properties.isCache());
return resolver;
}
#Bean
public SpringResourceTemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/jstemplates/");
resolver.setSuffix(".js");
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
resolver.setCharacterEncoding(UTF8);
return resolver;
}
}
A test javascript controller:
#Controller
public class JavascriptController {
#RequestMapping(method = RequestMethod.GET, value = "/test.js")
public String testjs() {
return "test";
}
}
The controller for the root page:
#Controller
public class MainController {
#RequestMapping(method = RequestMethod.GET, value = "/")
public String index(Model model) {
return "index";
}
}
There is a test.js file in the src/main/resources/jstemplates folder. If I try the url http://localhost:8080/test.js, it works as expected. But if I try, for example, the main url (http://localhost:8080/) it fails with the following error:
Caused by: java.io.FileNotFoundException: class path resource
[jstemplates/index.js] cannot be opened because it does not exist
It should be looking for the templates/index.html instead, so it seems the javascriptTemplateResolver overrides or takes precedence over de default one, without falling back to it.
Is there a way to add another template mode support integrated with the default Thymeleaf configuration, or I need to configure everything from scratch?
After some debugging I finally found the solution. It seems the SpringResourceTemplateResolver doesn't check by default if the template really exists. In my example configuration, the first template resolver is the one configured for the javascript templates, and it was creating a View without looking first if the template exists.
To solve it the template checkExistence property must be set to true. Ex:
#Bean
public SpringResourceTemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/jstemplates/");
resolver.setSuffix(".js");
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
resolver.setCharacterEncoding(UTF8);
resolver.setCheckExistence(true);
return resolver;
}
The only problem I see with this configuration is that if we create a js view with the same name as a html view, it will always get the javascript one.
Edit
To solve that last issue I've made some changes in the configuration:
Remove the .js suffix in the resolver configuration
When a controller gives a javascript view name, that name already includes the .js suffix.
The final configuration is as follows:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter
implements ApplicationContextAware {
private static final String UTF8 = "UTF-8";
#Autowired
private ThymeleafProperties properties;
#Autowired
private TemplateEngine templateEngine;
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Bean
public ThymeleafViewResolver javascriptThymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(this.templateEngine);
resolver.setCharacterEncoding(UTF8);
resolver.setContentType("application/javascript");
resolver.setViewNames(new String[] {"*.js"});
resolver.setCache(this.properties.isCache());
return resolver;
}
#Bean
public SpringResourceTemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(this.applicationContext);
resolver.setPrefix("classpath:/templates/js/");
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
resolver.setCharacterEncoding(UTF8);
resolver.setCheckExistence(true);
resolver.setCacheable(this.properties.isCache());
return resolver;
}
}
And the sample controller:
#Controller
public class JavascriptController {
#RequestMapping(method = RequestMethod.GET, value = "/js/test.js")
public String testjs() {
return "test.js";
}
}
The HTML view controllers remain unchanged.
i am trying to create a Spring MVC application with security included. All configuration is made in code, no XML-s. First, i have my WebApplicationInitializer, mapping all requests to my dispatchservlet:
public class DBCAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(DBCConfiguration.class);
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet(
"dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
servlet.setMultipartConfig(new MultipartConfigElement("", 1024*1024*5, 1024*1024*5*5, 1024*1024));
}
Also there is a config file:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "our.dbc")
public class DBCConfiguration extends WebMvcConfigurerAdapter {
private static final Logger log = Logger.getLogger(DBCConfiguration.class);
#Bean
public InternalResourceViewResolver getInternalResourceViewResolverJsp(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
viewResolver.setOrder(0);
log.info("#### Internal view resolver 0 called...");
return viewResolver;
}
#Bean
public StandardServletMultipartResolver multipartResolver(){
log.info("#### Multipart resolver called...");
return new StandardServletMultipartResolver();
}
// #Override
// public void addResourceHandlers(final ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/resources/**")
// .addResourceLocations("/resources/");
//
// }
//
// #Override
// public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// configurer.enable();
// }
}
As you can see, i have tried both addResourceHandler and configureDefaultServletHandling, but none of those worked. Problem is that all request by default, with lowest priority end up returing welcome page (so if bad address requested, welcome is returned :), so that instead of getting the css client gets back to welcome page:
#Controller
public class FileUploadController {
private static final Logger LOG = Logger.getLogger(FileUploadController.class);
private static final String FORM = "form";
private static final String WELCOME = "welcome";
private static final String DENIED = "accessDenied";
private static final String UPLOADED_REDIRECT = "redirect:/uploaded";
#Autowired
FileUploadService uploadService;
#RequestMapping(value = "/**", method = RequestMethod.GET)
public String getWelcome() {
return WELCOME;
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
LOG.info("#### /login called..." );
return "login";
}
#RequestMapping(value = { "/uploaded" }, method = RequestMethod.GET)
public String getUploaded() {
LOG.info("#### /uploaded called..." );
return "uploaded";
}
.
.
.
As i said, i also have security configured, but with default container servlet serving static resources, it whould not have to be changed. Anyway, i tried also to add permitall to resources, but no success. Anyway if i disable security it does not work either, so security is not the problem.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/welcome").permitAll()
.antMatchers("/form").access("hasRole('ADMIN')and hasRole('USER')")
.
.
.
But no chances. If i type http://localhost:8080/SpringMVC/resources/app.css instead of getting the stylesheet, browser gets back to welcome page.
Any suggestions? Any help whould be appreciated :)
I encounter this problem while I am trying to duplicate a simple spring OAuth project, sparklr2. source code here
https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2/sparklr
the source code runs perfectly, when I debug it with tomcat, it initialize all #Bean inside WebMvcConfigurerAdapter, including controllers. but noted that #ComponentScan() is not being used.
then I create my own MVC project, copy almost 100% of code, but I am using WebApplicationInitializer instead of AbstractDispatcherServletInitializer. I use WebApllicationInitializer because I have only learned this way to code MVC.
then I run the project, #Bean initialized. then I check /login with my browser, get 404. this could be caused by spring not knowing I have controllers, then I add #ComponentScan to my configuration class, /login now shows up.
but the weird thing is, all #Bean related to Controller, are not initialized. so, when I call any method to those controller, since their attributes are not initialized, gives me no object or null exception.
So, my point is, how does that sample works, I mean controller and jsp correctly handle and response without using #ComponentScan?
and look at it from different angle, why does #ComponentScan stop #Bean from being initialize in my project?
my WebApplicationInitializer
#Configuration
#EnableWebMvc
#ComponentScan("umedia.test.oauth.controller")
public class MvcConfig extends WebMvcConfigurerAdapter {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ContentNegotiatingViewResolver contentViewResolver()
throws Exception {
ContentNegotiationManagerFactoryBean contentNegotiationManager = new ContentNegotiationManagerFactoryBean();
contentNegotiationManager.addMediaType("json",
MediaType.APPLICATION_JSON);
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
MappingJackson2JsonView defaultView = new MappingJackson2JsonView();
defaultView.setExtractValueFromSingleKeyModel(true);
ContentNegotiatingViewResolver contentViewResolver = new ContentNegotiatingViewResolver();
contentViewResolver
.setContentNegotiationManager(contentNegotiationManager
.getObject());
contentViewResolver.setViewResolvers(Arrays
.<ViewResolver> asList(viewResolver));
contentViewResolver.setDefaultViews(Arrays.<View> asList(defaultView));
return contentViewResolver;
}
#Bean
public PhotoServiceImpl photoServices() {
List<PhotoInfo> photos = new ArrayList<PhotoInfo>();
photos.add(createPhoto("1", "marissa"));
photos.add(createPhoto("2", "paul"));
photos.add(createPhoto("3", "marissa"));
photos.add(createPhoto("4", "paul"));
photos.add(createPhoto("5", "marissa"));
photos.add(createPhoto("6", "paul"));
PhotoServiceImpl photoServices = new PhotoServiceImpl();
photoServices.setPhotos(photos);
return photoServices;
}
// N.B. the #Qualifier here should not be necessary (gh-298) but lots of
// users report needing it.
#Bean
public AdminController adminController(
TokenStore tokenStore,
#Qualifier("consumerTokenServices") ConsumerTokenServices tokenServices,
SparklrUserApprovalHandler userApprovalHandler) {
AdminController adminController = new AdminController();
adminController.setTokenStore(tokenStore);
adminController.setTokenServices(tokenServices);
adminController.setUserApprovalHandler(userApprovalHandler);
return adminController;
}
// this url, do I need to change it?
private PhotoInfo createPhoto(String id, String userId) {
PhotoInfo photo = new PhotoInfo();
photo.setId(id);
photo.setName("photo" + id + ".jpg");
photo.setUserId(userId);
photo.setResourceURL("/org/springframework/security/oauth/examples/sparklr/impl/resources/"
+ photo.getName());
return photo;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public PhotoServiceUserController photoServiceUserController(
PhotoService photoService) {
PhotoServiceUserController photoServiceUserController = new PhotoServiceUserController();
return photoServiceUserController;
}
#Bean
public PhotoController photoController(PhotoService photoService) {
PhotoController photoController = new PhotoController();
photoController.setPhotoService(photoService);
return photoController;
}
#Bean
public AccessConfirmationController accessConfirmationController(
ClientDetailsService clientDetailsService,
ApprovalStore approvalStore) {
AccessConfirmationController accessConfirmationController = new AccessConfirmationController();
accessConfirmationController
.setClientDetailsService(clientDetailsService);
accessConfirmationController.setApprovalStore(approvalStore);
return accessConfirmationController;
}
/* #Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}*/
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(
"/resources/");
}
}
so, you have #ComponentScan which interacts with #Controller on your controllers + still create #Bean's with those?
As a first step try to remove #Beans and try to inject dependencies using #Autowired on controllers' constructors. Then #ComponentScan should recognize #Controller, inject dependencies and use #RequestMapping without issues.
This is my AppInitializer:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.project.app")
public class AppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer implements
WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(container);
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
And this is my AppConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.acfcm.app")
#Import({ SecurityConfig.class })
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolverForClasses() {
ResourceBundleViewResolver viewResolver = new ResourceBundleViewResolver();
viewResolver.setOrder(1);
viewResolver.setBasename("views");
return viewResolver;
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setOrder(2);
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(
"/resources/");
}
}
What I want is how to change the AppConfig to put .jsp in differents foldes into the project because right now only can save it in /WEB-INF/views/ and there is a lot of .jsp!! I want to have two more folders to see my project like:
WEB-INF/views/moduleOne/
WEB-INF/views/moduleTwo/
...
Thanks!
I think your code is all fine; it will look for your JSPs starting in /WEB-INF/views/
Most people do like you say and break up JSP views under folders like /admin, /common, etc.
The way you do this is to specify the subfolder in the controller. For example, in your controller, you could return:
#RequestMapping(value = "/admin/index.htm", method = RequestMethod.GET)
public ModelAndView index(HttpServletRequest request,
HttpServletResponse response)
{
Map<String, Object> myModel = new HashMap<String, Object>();
myModel.put("someValues", new ArrayList());
ModelAndView mv = new ModelAndView("admin/index","model", myModel);
return mv;
}
Doing it like the above, you can put your JSP under
/WEB-INF/views/admin/index.jsp
Of course, your mappings (/admin) doesn't have to match the directory structure (/WEB-INF/views/admin) but we chose to make both match, to make it faster to find the code (if controller mapping matches the dir structure).
The important thing to remember is that whatever you put in that ModelAndView first param, Spring will prepend and append the values you defined in your code:
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
So, if you return ModelAndview("/pierre/was/here/something"), Spring will try to find the JSP at location: "/WEB-INF/views/pierre/was/here/something.jsp"
The common practice is to have the configuration as yours, and in the request handler controller methods, use the view names as moduleOne/view1, moduleTwo/view2 etc.