I am trying to make a documentation of my API with swagger.
My REST API is written with the help of spring and typical Controller looks like:
#Controller
#RequestMapping("/mymapping")
public class ParameterController {
#Autowired
Mydao myDao;
/**
* GET list
*
* #return The resulting string
*/
//produces replaced accept-header
#RequestMapping(value="/Parameter", method=RequestMethod.GET, produces={MediaType.APPLICATION_JSON_UTF8_VALUE, "application/ld+json"})
public #ResponseBody List<String> getAllParameters() {
List<String> params= myDao.getAllParameters();
return params;
}
}
To configurate the Swagger I followed the instruction here:
added a package in Maven
created a configuration Java class SwaggerConfig (I use only Java-configuration for Spring)
added resource Handlers to the existing class extending WebMvcConfigurerAdapter
added packages "io.swagger" and "springfox.documentation" to the #ComponentScan annotation of the same class extending WebMvcConfigurerAdapter
But each time when I call http://localhost:8080/spring-security-rest/api/v2/api-docs I receive
16:43:43,370 WARN qtp1072754634-13 servlet.PageNotFound:1136 - No mapping found for HTTP request with URI [/spring-security-rest/api/v2/api-docs] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-437a7f13'
The system searches the path /spring-security-rest/api/v2/api-docs among the controllers' mappings. And does not find it.
What did I forget?
Thank you in advance.
I solved mine by placing the SwaggerConfig Java Class in the same package where I had a the class with #SpringBootApplication.
Heres a suggestion:
Typically this is due to improper use of component scan. You can read more about component scan here (read the 2 questions fully :) )
Click here for the 2 questions
my SwaggerConfig class look like this:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket beerMapApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(regex("/api.*"))
.build()
.pathMapping("/")
.apiInfo(apiInfo());
}
#Bean
public UiConfiguration uiConfig() {
return UiConfiguration.DEFAULT;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("title")
.description("description")
.version("0.1")
.build();
}
}
and this is how my WebMvcConfigurerAdapter.addResourceHandlers looks:
#Configuration
#EnableWebMvc
#ComponentScan({ "com.something.server.controller" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
registry
.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
Related
Below is my class in which i had to use both #Configuration and #Controller as there should be only one instance of Thymeleaf in the entire application else i get exceptions for that. My other classes are annotated with #RequestScope so i cannot use a singleton scoped bean. So i had a mixup of Configuration and Controller to get the result, but i feel it is a bad practice. I would appreciate any help to refactor the code and remove the bad practice.
UPDATE
I am using spring-boot 1.5.14. I am using the following approach to process a template and keep the processed template as string.
#Controller
#Configuration
#EnableWebMvc
#ApplicationScope
public class MyThymeleafConfig {
#GetMapping("/view-template")
#ResponseBody
public void viewTemplates() {
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine().process("templates/view-to-process.html", context);
System.out.println(html);
}
/*
configuration for thymeleaf and template processing
*/
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:");
templateResolver.setSuffix(".html");
templateResolver.setCacheable(false);
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
}
To serve static resources the following config:
#Configuration
#EnableWebMvc
public class StaticResourceConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/**")
.addResourceLocations("/static/", "classpath:static/");
}
}
UPDATE
I have also mentioned the reasons why i couldn't accept the below mentioned answers as my other classes have request scopes.
UPDATE
I have other classes with #RequestScopelike below:
#RequestScope
#Controller
public class SecondController {
#GetMapping("/viewPage")
public String viewPage(Model model) {
model.addAttribute("mydata", "sjfbsdf");
model.addAttribute("somedata", "sjdfksfjhshgdfbskdfj");
return "templates/view-to-process.html";
}
}
Assuming you're using Spring Boot, since you have it in tags, you do not need any configuration to use Thymeleaf.
By just having this dependency, you can:
#GetMapping("/view-template")
public String viewTemplates(Model model) {
model.addAttribute("mydata", "this is it")
return "view-to-process";
}
And it should work.
By the way, yes, having #Configuration and #Controller in the same class is something you should never need.
If you see the source codes of the annotations (Spring 5) you have:
Controller
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* #return the suggested component name, if any (or empty String otherwise)
*/
#AliasFor(annotation = Component.class)
String value() default "";
}
Configuration
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated
* with this Configuration class. If left unspecified (the common case),
* a bean name will be automatically generated.
* <p>The custom name applies only if the Configuration class is picked up via
* component scanning or supplied directly to a {#link AnnotationConfigApplicationContext}.
* If the Configuration class is registered as a traditional XML bean definition,
* the name/id of the bean element will take precedence.
* #return the suggested component name, if any (or empty String otherwise)
* #see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
#AliasFor(annotation = Component.class)
String value() default "";
}
you notice that they are the same (they both include the more generic #Component annotation). So it doesn't make sense to use them both by seeing this fact. Another thing, more important, is that spring is trying to give a sort of tags meaning of these annotations that should describe the use.
The Configuration is used to wire in necessary parts to the application to function properly, at startup phase.
The Controller is used to define a class which is serving as an interface to the outside world, i.e: how can other actors use your application.
As you can see, it makes very little sense to use those 2 together.
Take look at Spring Boot documentation typical layout
Also this article SOLID Programming Principles
And look at Spring Boot guide Spring Boot Thymeleaf (you don't need your #Bean configurations)
In two words you should separate
1. MyThymeleafConfig configuration
2. TemplateController with viewTemplates() and another endpoints
To refactor them, it's easy and straight forward to separate #Bean methods to a separate #Configuration class:
#Configuration
// #Controller is redundant as we have #Configuration
// #EnableWebMvc is also redundant since you already annotate it in other class
// #ApplicationScope is also redundant since you do not need to create bean of MyThymeleafConfig anymore
public class MyThymeleafConfig {
/*
configuration for thymeleaf and template processing
*/
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:");
templateResolver.setSuffix(".html");
templateResolver.setCacheable(false);
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
}
#Configuration class return the same bean instance, regardless how many times you call the bean methods.
Now in your controller:
#Controller
public class MyThymeleafConfig {
#Autowired
private SpringTemplateEngine templateEngine;
#GetMapping("/view-template")
#ResponseBody
public void viewTemplates() {
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine.process("templates/view-to-process.html", context);
System.out.println(html);
}
}
But honestly, I don't know why you have to manually interact with TemplateEngine / SpringTemplateEngine, since Spring-thymeleaf will automatically process the template with given variable for you. (Like #sedooe example)
Don't put request mappings inside configuration classes, it violates the principal of separation of concerns. You can go for a approach like below.
All the application wide beans are setup in Application class which is present in the root of the classpath. Application class in the best place to have your thymeleaf and static resource configurations too, since the Application class have application-scope.
#SpringBootApplication
#EnableWebMvc
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
resolver.setCache(false);
return resolver;
}
#Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setEnableSpringELCompiler(true);
templateEngine.addDialect(new LayoutDialect());
templateEngine.addDialect(new Java8TimeDialect());
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new
SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/templates/");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
}
If you put the static resources inside a folder named static or public in the classpath, springboot identify that as the location for static resources. Then you don't need to override addResourceHandlers method. If you really want to do it, you can do it inside the Application class extending WebMvcConfigurerAdapter. You don't need separate class to configure just static resource paths.
Don't put request mappings inside configuration classes, put them in separate controller classes like:
#Controller
public class MyController {
#GetMapping("/view-template")
#ResponseBody
public void viewTemplates() {
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine().process("templates/view-to-process.html", context);
System.out.println(html);
}
}
Of cause, springboot allows you to do it the way you like, but you'd better stick to a general approach.
These two annotations are for different things, thus it's better to not use them on the same class. Cause it's against Separation of Concerns principal.
I have been trying out the Java Configuration feature of Spring Web Flow 2.4 by modifying an existing project from xml configuration to JavaConfig. The XML version works, but JavaConfig doesn't. Every time I try to start the flow with URL http://localhost:8080/sia_p219_ch08_spring_web_flow_order_pizza_customer_flow_complete/pizza , it returns 404. There are no exceptions. The console show no "no request mapping found for..." message. The webpage shows The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
The project is hosted on github, the working XML version is here.
I think the problem is the request URL doesn't call the pizza flow (/WEB-INF/flows/pizza/pizza-flow.xml).
Here are some code snippets:
WebAppInitializer:
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
// map DispatcherServlet to /
protected String[] getServletMappings() {
return new String[] { "/" };
}
RootConfig:
#Configuration
#Import({WebFlowConfig.class})
public class RootConfig {}
WebFlowConfig:
#Configuration
#ComponentScan({"pizza"})
public class WebFlowConfig extends AbstractFlowConfiguration {
static{
System.out.println("WebFlowConfig loaded");
}
#Autowired
private WebConfig webMvcConfig;
#Bean
public FlowDefinitionRegistry flowRegistry() {
return
getFlowDefinitionRegistryBuilder(flowBuilderServices())
.setBasePath("/WEB-INF/flows")
.addFlowLocationPattern("/**/*-flow.xml")
.build();
}
#Bean
public FlowExecutor flowExecutor(){
return getFlowExecutorBuilder(flowRegistry()).build();
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setDevelopmentMode(true)
.build();
}
#Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
factoryCreator.setViewResolvers(Collections.singletonList(this.webMvcConfig.viewResolver()));
factoryCreator.setUseSpringBeanBinding(true);
return factoryCreator;
}
}
WebConfig
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
static{
System.out.println("WebConfig loaded");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/flows/");
resolver.setSuffix(".jsp");
return resolver;
}
// configure static content handling
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
The flow definition files and JSPs are fine and you can see them on github if you want.
Thanks a lot, any help is greatly appreciated.
What I've found so far that the configuration definitely lacks this part of configuration in WebFlowConfig (take a look at the documentation page for integration with Spring MVC for details):
#Bean
#Autowired
public FlowHandlerAdapter flowHandlerAdapter(FlowExecutor flowExecutor) {
FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor);
return flowHandlerAdapter;
}
#Bean
#Autowired
public FlowHandlerMapping flowHandlerMapping(FlowDefinitionRegistry flowDefinitionRegistry) {
FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
flowHandlerMapping.setFlowRegistry(flowDefinitionRegistry);
flowHandlerMapping.setOrder(0);
return flowHandlerMapping;
}
Also remove mvcViewFactoryCreator definition and setViewFactoryCreator call from the flowBuilderServices bean definition as well. It works for me.
EDIT: I realized I made a mistake in my ComponentScan as a lot of commenters pointed out, I changed it but it is still not working (still 404).
I have a project and I'm using all annotations-based configuration. Here is the configuration files:
WebConfig.java
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "src.controller")
public class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
WebInitializer.java
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
I also have a persistence config but I don't think that's relevant to this problem. Anyways, here is my root path controller:
AuthorController.java
#Controller
#RequestMapping({"/authors", "/"})
public class AuthorController {
#Autowired
AuthorService authorservice;
#RequestMapping(method=RequestMethod.GET)
public String getAuthors(ModelMap model){
System.out.println("----------called--------------"); //not printed
List<Author> authors = authorservice.getAllAuthors();
model.addAttribute("authors", authors);
return "authors";
}
}
The controller never gets called and I end up getting a 404 error.
Can you tell the name of the package to which 'AuthorController' belongs ? I think issue is with #ComponentScan(basePackages = "src"). Here you should add package name of the controller classes.
#ComponentScan(basePackages = "com.sample.app")
#ComponentScan(basePackages = "com.sample.*")
#ComponentScan(basePackages = "com.*")
All the above are valid entries for ComponentScan annotation.
The basePackages property of #ComponentScan annotation doesn't represent the folder where your java code is. It represents the root package of java classes where Spring should scan for Spring beans.
So, if you have your controllers in com.myapp.web.controllers then configure it as follows:
#ComponentScan(basePackages = "com.myapp.web.controllers")
I am using spring again after a longer period and happy to see that xml configurations are no longer required in every case.
I want to build a RESTful App, but I still have to deliver the frontend app. I figured the simplest way without using any additional template engines like thymeleaf would be serving a static jsp.
I'm using the project template from start.spring.io with just spring-mvc as dependency, thus i'm using spring boot as well.
I wrote a controller in order to deliver the jsp, but it seems that the mapping for the views has to be configured first.
#Controller
public class StaticPagesController {
#RequestMapping(value = "/")
public String index(){
return "index";
}
}
So i created a configuration class:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "de.tuberlin.sense.emp")
public class WebConfiguration {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
return viewResolver;
}
}
index.html is located in main/webapp/WEB-INF/views/
When i send a request to /, I get a WARN in the logs wich states No mapping found for HTTP request with URI [/WEB-INF/views/index.html] in DispatcherServlet with name 'dispatcherServlet'
What am I missing? Can I do this without any xml configuration?
Here is my main application class code:
UPDATE:
#SpringBootApplication
public class ExperimentManagementPlatformApplication {
public static void main(String[] args) {
SpringApplication.run(ExperimentManagementPlatformApplication.class, args);
}
}
Try adding a view controller:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
Reference
It seems to me that your controller is not being detected, I would suggest to check your file structure, is the StaticPagesController controller in the de.tuberlin.sense.emp package? (as stated in:)
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "de.tuberlin.sense.emp")
public class WebConfiguration
I was able to serve the static page without needing any controller at all by just adding the html file to the static directory. Furthermore i found out that the entire webapp directory is not included when the app is deployed as jar which explains why the files in there could not be found.
The Spring documentation
Your config class should extend WebMvcConfigurerAdapter class
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "de.tuberlin.sense.emp")
public class WebConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
return viewResolver;
}
}
For this question, say, we have a MVC configuration class:
#Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
#Bean
public ViewResolver viewResolver() {
return new InternalResourceViewResolver();
}
// bla-bla
}
I have several bean definitions as usual in a configuration class. The question is while enabling some features such as #EnableCaching, #EnableMBeanExport, #EnableAspectJAutoProxy, how can i control enabling process of those features, ie:
#Configuration
// #EnableCaching instead of this
public class MvcConfig extends WebMvcConfigurationSupport {
#Autowired
SomeResource someResource;
#Bean
public SomeBean someBean() {
if(someResouce.someEvaluation(someParam)) {
enableCaching(); // is that enabling possible?
}
}
#Bean
public ViewResolver viewResolver() {
return new InternalResourceViewResolver();
}
// bla-bla
}
NOTE: I'm using spring 3.2.x