Is there any configuration option that allows to change base url only for rest controllers, for example if my api's base url is www.example.com/user/{id} becomes www.example.com/rest/user/{id} ?
I am using spring boot v1.3.2
I tried to create custom annotation which extends RestController by adding RequestMapping.
Here is the example, but it does not work.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#RestController
#RequestMapping(value = "/rest", path = "/rest")
public #interface MyRestController { }
Option 1: Custom Annotation
Create a Custom Annotation that declares the base URL and use that in lieu of #RestController.
CustomRestControllerAnnotation.java
package com.example.stackoverflow.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#RestController
#RequestMapping("/rest")
public #interface CustomRestControllerAnnotation {}
FirstRestController.java
package com.example.stackoverflow.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.stackoverflow.config.CustomRestControllerAnnotation;
#CustomRestControllerAnnotation
public class FirstRestController {
#RequestMapping("/first")
public String firstMethod(){
return "First Controller";
}
}
SecondRestController.java
package com.example.stackoverflow.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.stackoverflow.config.CustomRestControllerAnnotation;
#CustomRestControllerAnnotation
public class SecondRestController {
#RequestMapping("/second")
public String secondMethod(){
return "Second Controller";
}
}
Option 2: Base RestController
By creating a Base Controller that serves as a template for all of your actual Controllers, you can effectively manage the root URL from a single location.
BaseRestController.java
package com.example.stackoverflow.controller;
import org.springframework.web.bind.annotation.RequestMapping;
#RequestMapping("/rest")
public class BaseRestController {}
Then you simply extend this class for all of your actual Controllers.
FirstRestController.java
package com.example.stackoverflow.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class FirstRestController extends BaseRestController{
#RequestMapping("/first")
public String firstMethod(){
return "First Controller";
}
}
SecondRestController.java
package com.example.stackoverflow.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SecondRestController extends BaseRestController{
#RequestMapping("/second")
public String secondMethod(){
return "Second Controller";
}
}
Option 3: Spring Data REST
If your Controllers are serving Data from a Repository, then Spring Data REST can take out much of the boilerplate & solve your initial problem.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
By declaring this dependency, all of your Repositories automatically become REST enabled.
You can control the base URL by using a property file.
application.properties
spring.data.rest.basePath=/rest
Updated config for Spring Boot v2.1.0
In Spring Boot v2.1.0 you can configure base URL in application.properties like
server.servlet.context-path = /baseApiName
Complete property configuration list
Typically you would define a servlet that handles all (or a particular set) of your restful requests. You would then tell that servlet to listen to a particular URL pattern like /rest. The #RequestMapping annotations of your controllers are unaware of that 'top level' pattern.
For instance, when bootstrapping your Spring Web Application, you could create that restful servlet manually and add a mapping. The whole setup is a little too large to be posted here, but find a snippet below to get a notion.
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
...
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
...
ServletRegistration.Dynamic restfulServlet = servletContext.addServlet("myServlet", new DispatcherServlet(rootContext));
restfulServlet.addMapping("/rest/*");
...
}
You should add server.servlet-path=/api on your application.properties file and all requests you should send like domain/api/users/{id}
Related
So I've got a simple spring boot app, #SpringBootApplication, a stub #Configuration and a #RestController all in the same package. Spring-boot-web-starter is there and the webserver comes up fine, actuator endpoints and all. But I cannot for the life of me get the app to pick up the #RestControllers.
enter image description here
Main.class:
package com.iglossolalia.munin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(MuninContext.class, args);
}
}
MuninContext.class:
package com.iglossolalia.munin;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableAutoConfiguration
public class MuninContext {
}
MuninService.class:
package com.iglossolalia.munin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class MuninService {
private static final Logger LOG = LoggerFactory.getLogger(MuninService.class);
public MuninService() {
LOG.info("Started MuninService");
}
#GetMapping("/health")
public String healthCheck() {
return "pong";
}
}
Tried explicitly adding the rest controller to the component scan with no luck there.
You have no #ComponentScan annotation in your MuninContext. Actually you can write SpringApplication.run(Main.class, args) in main method as Spring Initializr generate by default, you don't really need your context, because #SpringBootApplication work as configuration and contains #EnableAutoConfiguration, #ComponentScan, and some other annotations. Otherwise, as you pass your config class as argument in SpringApplication.run method, annotation #SpringBootApplication in Main class has no effect
i'm trying to inject services in Web service Class like this
package com.mobinets.web.nep.backend.soapControllers;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import com.mobinets.web.nep.backend.data.entity.Router;
import com.mobinets.web.nep.backend.data.entity.Version;
import com.mobinets.web.nep.backend.services.RouterService;
import com.mobinets.web.nep.backend.services.VersionService;
#Service
#WebService
public class RouterSoapService extends SpringBeanAutowiringSupport{
#Autowired
private RouterService routerService;
#Autowired
private VersionService versionService;
#WebMethod
public String getRouter(#WebParam int objectId, #WebParam String versionName) {
Version version = versionService.findByName(versionName);
Router router = routerService.findByObjectIdAndVersion(objectId, version.getId());
return router.getName();
}
}
I extended the class from SpringBeanAutowiringSupport and add #Service annotation,
it keeps giving me null on versionService and routerService,
am I missing something ?
Only reason I can think of would be if either of the services
are missing one of the #Component stereotypes (you say they both have #Service
so this should be fine) or
if they rely on any other components which aren't autowired for example if
either has a repository which has been instantiated with the new keyword
they would be excluded from autowiring
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 3 years ago.
I am developing a spring boot application to send sms notification. This is my class for the purpose.
package org.otp.services;
import org.otp.Configurations;
import com.mashape.unirest.http.HttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
#Component
public class SmsService
{
private static final Logger LOG = LoggerFactory.getLogger(SmsService.class);
public String send(String mobile, String msg)
{
//Code
}
}
And this is the class which uses the above class for sending notification.
package org.otp.controllers;
import org.otp.Constants;
import org.otp.services.EmailService;
import org.otp.services.SmsService;
import org.otp.dto.MessageRequest;
import org.otp.dto.MessageResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
#Component
public class MessageController {
private static final Logger LOG = LoggerFactory.getLogger(MessageController.class);
#Autowired
SmsService smsService;
public void sendMessageToAlert(#RequestBody MessageRequest messageRequest)
{
String smsStatus = "FAIL";
MessageResponse messageResponse = new MessageResponse();
//1. Nullpointer
smsStatus = smsService.send(messageRequest.getMobileNo(),messageRequest.getMessage());
}
}
Main Class
package org.otp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
#SpringBootApplication
#EnableAsync
public class OtpServiceApplication implements ApplicationRunner
{
public static void main(String[] args) {
SpringApplication.run(OtpServiceApplication.class, args);
}
}
Problem is, I get a nullpointer exception in the (1) stating that my SmsService object is null. And my main class is in package org.otp so the two classes here falls under sub package so no need of component scan.
Therefore I am confused what to do to solve this. I have tried many answers here like adding a #Component annotation and #ComponentScan in main class but nothing works. Could someone please point out my mistake here.
Thanks in advance.
If your #Autowired annotation is not working and throws NPE ,it means that spring fails to create an instance of the component class in the application context . Try to:
Verify that the classes are in class path for scanning and also check to ensure that all auto-wired classes have the annotation #Component to enable them to be picked up during class path scanning.
Check the spring boot start up logs to verify if there are any errors
during bean creation.
Check to ensure all related classes used in the service layer are auto-wired properly and that the injected classes are annotated with #Component .
For further help please share the main application class along with your project structure.
Since you are using springboot , it is preferable to use the sprinboot stereotype annotations instead of the #Component annotation, if you are building a standard springboot web application.
#Service : for the service layer.
#Controller : for the controller layer . Also,DispatcherServlet will look for #RequestMapping on classes which are annotated using #Controller but not with #Component.
In Springboot application's main class add following annotation
#SpringBootApplication
#ComponentScan(
basePackages = {"org.otp.*"}
)
public class YourSpringMainClass{
public static void main(String[] args) {
SpringApplication.run(YourSpringMainClass.class, args);
}
}
While using annotations we should configured with #ComponentScan annotation to tell Spring the packages to scan for annotated components. This should be used in mail class(Which class wants to load first) in your case you are working with spring boot so you should use this annotation in Springboot application's main class. Like below
#SpringBootApplication
#ComponentScan(
basePackages = {"org.otp.*"}
)
public class YourSpringMainClass{
public static void main(String[] args) {
SpringApplication.run(YourSpringMainClass.class, args);
}
}
I would like to assign a value with the name of the #Service from application.properties tu |#Qualifier . I tried but it doesn't work .
In fact i have two services which implement the same interface and i would like to change the service from application.properties
Someone has any idea how to do this?
this is my code
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class Controler {
#Qualifier("${service.name}")
#Autowired
private InterfaceTest interfaceTest;
#GetMapping("/test")
public String test(){
return interfaceTest.test();
}
}
Thank you very much for your help
I think you can do that in the constructor of this controller by using Environment and ApplicationContext beans.
Just remove Autowired annotation from fields and accept other parameters through constructor as well.
public Controller(Environment environment, ApplicationContext applicationContext) {
String serviceName = environment.getProperty("service.name");
this.interfaceTest = applicationContext.getBean(serviceName, InterfaceTest.class);
}
Those bean qualifiers and other annotations of spring just except literals as far as I know.
Hope this helps.
my problem is that i dont know how to specify the path for my Controller package to search for requestmapping annotations inside it in the #componenscan annotation, which is in the main class.
Project structure:
My DemoApplication class:
package com.personalitymeet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Configuration
#EnableAutoConfiguration
#ComponentScan()
#Controller
public class DemoApplication {
#ResponseBody
#RequestMapping("/")
String entry(){
return "bla";
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Usercontroller.java:
package com.personalitymeet.web;
import com.personalitymeet.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by mv on 15.09.2016.
*/
#Controller
public class UserController {
#RequestMapping("/user")
public String user(Model model){
User user = new User();
user.setFirstname("Misi");
user.setLastname("Varga");
model.addAttribute("user",user);
return "userview";
}
}
So, my question is, how can i tell springboots that it should search for #Requestmapping annotation in the Usercontroller class?
You can specify component scan to your configuration file to scan folder com.personalitymeet and it will automatically pick all the classes which has relevant annotations.
Below are basic 3 annotations needed to init application.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.personalitymeet")
Your problem isn't the #ComponentScan, which will search in com.personalitymeet and all subpackages. It's that for some reason you don't have the MVC infrastructure activating.
You should have spring-boot-starter-web as a dependency in your build.gradle file, and you should add #EnableWebMvc to your configuration class.
Can you replace this code:
#Configuration
#EnableAutoConfiguration
#ComponentScan()
#Controller
public class DemoApplication {
...
With this code:
#SpringBootApplication
#Controller
public class DemoApplication {
...
Configure your application to expose the management endpoints:
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#production-ready
and then hit this endpoint to see the endpoint exposed by the application:
http://localhost:[port]/manage/mappings
Also, I recommend to create a separate controller for the root to keep your startup class uncluttered with application logic.