I have a project setup with Spring Boot and Jersey, and I need to serve static content. The main difference with other similar questions is the fact that "mywebsite.com/" has to be a Jersey resource. This is my current JerseyConfig:
#ApplicationPath("/")
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("com.mywebsite.services.rest",
"com.mywebsite.services.publication");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
And my publication resource:
#Component
#Path("/")
#Produces(MediaType.TEXT_HTML)
public class PublicacionResource {
#GET
public Template index() throws Throwable {
return generateTemplateWithParameters("instalaciones", null, null);
}
...
}
Jersey doesn't seem to be forwarding the 404 requests, as I cannot access static content.
Throw an Exception in generateTemplateWithParameters and forward it via ExceptionMapper
#Provider
public class MyExceptionProvider implements ExceptionMapper<MyException> {
#Override
public Response toResponse(final MyException exception) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
Don't forget to register it in Jersey's ResourceConfig:
register(MyExceptionProvider.class);
Related
I migrate the code to springboot and our API works well. Only interceptor can't be triggerred.
I googled related solutions and modify the code to right format which still failed to trigger the interceptor.
In our project, we also have the filter which extends OncePerRequestFilter and works.
It makes me confused.
They should be no big difference.
Btw, AOP is used in the project.
It's my code.
JerseyConfig.class
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(){
packages("com.xxx");
}
}
VaultAuthorizationInterceptor.class
#Component
public class VaultAuthorizationInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(VaultAuthorizationInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("test");
return true;
}
}
VaultAuthConfig.class
#Configuration
public class VaultAuthConfig implements WebMvcConfigurer {
#Bean
public VaultAuthorizationInterceptor getVaultInterceptor() {
return new VaultAuthorizationInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getVaultInterceptor()).addPathPatterns("/**");
}
}
When you are using the spring-boot-starter-jersey, you use jersey as your web stack. That means any requests will processed by jersey. So you have to register a jersey filter or interceptor. Take a look at the jersey documantation. There is described how to use filters and interceptors. I think you want to use a filter because interceptors in the jersey stack used to manipulate the input or output stream.
This is my jersey config class
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeService.class);
}
}
Here autowiring of employeeService is giving a null pointer exception
#Path("/ems")
#Component
public class EmployeeRestController {
#Autowired
private EmployeeService employeeService;
#GET
#Path("/employees")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
}
I have tried everything
In my employeeServiceImpl I have #service annotation
Still, it is not working.
To configure the dependency injection using the built in DI framework (HK2), you should use an AbstractBinder, as mentioned in some answers in Dependency injection with Jersey 2.0.
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.ems");
register(new AbstractBinder() {
#Override
protected void configure() {
bind(EmployeeService.class)
.to(EmployeeService.class)
.in(Singleton.class);
}
});
}
}
Secondly, you do not use the #Autowired annotation. This annotation is specifically for Spring. For standard injection with Jersey, just use the #Inject annotation. Also remove the #Component annotation, as this is also for Spring.
As an aside, if you do want to integrate Spring with Jersey, you should read Why and How to Use Spring With Jersey. It will break down what you need to understand about integrating the two frameworks.
You should register Controller not Service class.
Sample
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeRestController.class);
}
}
I am using Spring Boot(1.5.3) to develop a REST Web Service. In order to take some action on incoming request I have added an interceptor shown below.
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Autowired
RequestParser requestParser;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//HandlerMethod handlerMethod = (HandlerMethod) handler;
requestParser.parse(request);
return true;
}
}
RequestInterceptor has an autowired Spring Bean RequestParser responsible for parsing the request.
#Component
public class RequestParserDefault implements RequestParser {
#Override
public void parse(HttpServletRequest request) {
System.out.println("Parsing incomeing request");
}
}
Interceptor registration
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/usermanagement/v1/**");
}
}
And my Spring Boot Application
#SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
Now when a request comes in, it lands in preHandle method of RequestInterceptor but RequestParser is NULL. If I remove the #Component annotation from RequestParser I get an error during Spring context initialization No bean found of type RequestParser. That means RequestParser is registered as Spring bean in Spring context but why it is NULL at the time of injection? Any suggestions?
Your problem lies in this new RequestInterceptor().
Rewrite your WebMvcConfig to inject it, e.g. like this:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor)
.addPathPatterns("/usermanagement/v1/**");
}
}
I got a working spring boot rest service. When the path is wrong it doesn't return anything. No response At all. At the same time it doesn't throw error either. Ideally I expected a 404 not found error.
I got a GlobalErrorHandler
#ControllerAdvice
public class GlobalErrorHandler extends ResponseEntityExceptionHandler {
}
There is this method in ResponseEntityExceptionHandler
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, null, headers, status, request);
}
I have marked error.whitelabel.enabled=false in my properties
What else must I do for this service to throw a 404 not found response back to clients
I referred a lot of threads and don't see this trouble faced by anybody.
This is my main application class
#EnableAutoConfiguration // Sprint Boot Auto Configuration
#ComponentScan(basePackages = "com.xxxx")
#EnableJpaRepositories("com.xxxxxxxx") // To segregate MongoDB
// and JPA repositories.
// Otherwise not needed.
#EnableSwagger // auto generation of API docs
#SpringBootApplication
#EnableAspectJAutoProxy
#EnableConfigurationProperties
public class Application extends SpringBootServletInitializer {
private static Class<Application> appClass = Application.class;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(appClass).properties(getProperties());
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public FilterRegistrationBean correlationHeaderFilter() {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(new CorrelationHeaderFilter());
filterRegBean.setUrlPatterns(Arrays.asList("/*"));
return filterRegBean;
}
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
static Properties getProperties() {
Properties props = new Properties();
props.put("spring.config.location", "classpath:/");
return props;
}
#Bean
public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
WebMvcConfigurerAdapter webMvcConfigurerAdapter = new WebMvcConfigurerAdapter() {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).favorParameter(true).parameterName("media-type")
.ignoreAcceptHeader(false).useJaf(false).defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON);
}
};
return webMvcConfigurerAdapter;
}
#Bean
public RequestMappingHandlerMapping defaultAnnotationHandlerMapping() {
RequestMappingHandlerMapping bean = new RequestMappingHandlerMapping();
bean.setUseSuffixPatternMatch(false);
return bean;
}
}
The solution is pretty easy:
First you need to implement the controller that will handle all error cases. This controller must have #ControllerAdvice -- required to define #ExceptionHandler that apply to all #RequestMappings.
#ControllerAdvice
public class ExceptionHandlerController {
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(value= HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorResponse requestHandlingNoHandlerFound() {
return new ErrorResponse("custom_404", "message for 404 error code");
}
}
Provide exception you want to override response in #ExceptionHandler. NoHandlerFoundException is an exception that will be generated when Spring will not be able to delegate request (404 case). You also can specify Throwable to override any exceptions.
Second you need to tell Spring to throw exception in case of 404 (could not resolve handler):
#SpringBootApplication
#EnableWebMvc
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
}
}
Result when I use non defined URL
{
"errorCode": "custom_404",
"errorMessage": "message for 404 error code"
}
UPDATE: In case you configure your SpringBoot application using application.properties then you need to add the following properties instead of configuring DispatcherServlet in main method (thanks to #mengchengfeng):
spring.mvc.throw-exception-if-no-handler-found=true
spring.web.resources.add-mappings=false
I know this is an old question but here is another way to configure the DispatcherServlet in code but not in the main class. You can use a separate #Configuration class:
#EnableWebMvc
#Configuration
public class ExceptionHandlingConfig {
#Autowired
private DispatcherServlet dispatcherServlet;
#PostConstruct
private void configureDispatcherServlet() {
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
}
}
Please not that this does not work without the #EnableWebMvc annotation.
Add this to your Properties file.
spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
In your #ControllerAdvice class add this:
#ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<Object> handleNoHandlerFound404() {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);;
}
How can/should I pass an object from a ContainerRequestFilter to a (post-matching) resource in (JAX-RS) Resteasy version 3.0.11 that has undertow embedded and uses Guice?
The method ContainerRequestContext#setProperty stores values which are synced with the HttpServletRequest. So with plain JAX-RS you can store an attribute like this:
#Provider
public class SomeFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setProperty("someProperty", "someValue");
}
}
And afterwards you can obtain it in your resource class:
#GET
public Response someMethod(#Context org.jboss.resteasy.spi.HttpRequest request) {
return Response.ok(request.getAttribute("someProperty")).build();
}
With CDI you also can inject any bean in the filter and resource class:
#Provider
public class SomeFilter implements ContainerRequestFilter {
#Inject
private SomeBean someBean;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
someBean.setFoo("bar");
}
}
In your resource class:
#Inject
private SomeBean someBean;
#GET
public Response someMethod() {
return Response.ok(someBean.getFoo()).build();
}
I'd expect the same to be working with Guice.
Update: As #bakil pointed out correctly you should use a #RequestScoped bean if the object you want to pass should only be associated with the current request.