I hope you can help me with this.
I'm using Spring MVC (3.1.1) in my web application, and am facing a strange situation.
I have this simple #Controller, that makes use of the ServicioUsuario Service, and works fine:
#Controller
#RequestMapping("/ajax")
public class ControladorAjax extends ControladorGenerico {
#Autowired
ServicioUsuario servicioUsuario;
#RequestMapping("/check")
public ResponseEntity<String> check(#RequestParam String email) {
// Declarations and other operations omitted...
// Use servicioUsuario
servicioUsuario.doStuff();
return response;
}
}
However, if I remove the #Autowiring, and try to make Spring inject servicioUsuario as a parameter (i.e. by changing the method signature to: public ResponseEntity<String> check(#RequestParam String email, ServicioUsuario servicioUsuario)) the whole thing breaks, and I get this sort of exceptions in Tomcat's log:
javax.servlet.ServletException: NestedServletException in java.lang.Thread.getStackTrace:: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.package.ServicioUsuario]: Specified class is an interface
I have these interfaces:
com.package
|-> Servicio.java (interface)
|-> ServicioUsuario.java (interface that extends Servicio)
and these clases:
com.package.impl
|-> ServicioImpl.java (implements Servicio)
|-> ServicioUsuarioImpl.java (#Service("servicioUsuario") that extends ServicioImpl implements ServicioUsuario)
and configured Spring to scan both packages with:
<context:component-scan base-package="com.package
com.package.impl" />
Why is Spring trying to instantiate the interface and not the implementing class? Is it something I'm doing wrong?
According to 16.3.3.1 Supported method argument types of the official documentation, this is a complete list of what controller methods can take:
Request or response objects (Servlet API). [...] for example ServletRequest or HttpServletRequest.
Session object (Servlet API): of type HttpSession.
org.springframework.web.context.request.WebRequest or org.springframework.web.context.request.NativeWebRequest.
java.util.Locale for the current request locale [...]
java.io.InputStream / java.io.Reader for access to the request's content. [...]
java.io.OutputStream / java.io.Writer for generating the response's content. [...]
java.security.Principal containing the currently authenticated user.
#PathVariable annotated parameters for access to URI template variables. [...]
#RequestParam annotated parameters for access to specific Servlet request parameters. [...]
#RequestHeader annotated parameters for access to specific Servlet request HTTP headers. [...]
#RequestBody annotated parameters for access to the HTTP request body. [...]
#RequestPart annotated parameters for access to the content of a "multipart/form-data" request part. [...]
HttpEntity<?> parameters for access to the Servlet request HTTP headers and contents. [...]
java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap [...]
org.springframework.web.servlet.mvc.support.RedirectAttributes [...]
Command or form objects to bind request parameters to bean properties (via setters) or directly to fields, [...]
org.springframework.validation.Errors / org.springframework.validation.BindingResult [...]
org.springframework.web.bind.support.SessionStatus [...]
org.springframework.web.util.UriComponentsBuilder [...]
As you can see, Spring beans are not on this (quite impressive) list. Why would you inject services via controller method? They never change. It's enough to inject them once and assign them to a field.
Related
I'm running a GraphQL API using GraphQL-SPQR and Spring Boot.
At the moment, I am throwing RuntimeExceptions to return GraphQL errors. I have a customExceptionHandler that implements DataFetcherExceptionHandler that returns errors in the correct format, as shown below:
class CustomExceptionHandler : DataFetcherExceptionHandler {
override fun onException(handlerParameters: DataFetcherExceptionHandlerParameters?): DataFetcherExceptionHandlerResult {
// get exception
var exception = handlerParameters?.exception
val locations = listOf(handlerParameters?.sourceLocation)
val path = listOf(handlerParameters?.path?.segmentName)
// create a GraphQLError from your exception
if (exception !is GraphQLError) {
exception = CustomGraphQLError(exception?.localizedMessage, locations, path)
}
// cast to GraphQLError
exception as CustomGraphQLError
exception.locations = locations
exception.path = path
val errors = listOf<GraphQLError>(exception)
return DataFetcherExceptionHandlerResult.Builder().errors(errors).build()
}
}
I use the CustomExceptionHandler as follows (in my main application class):
#Bean
fun graphQL(schema: GraphQLSchema): GraphQL {
return GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy(CustomExceptionHandler()))
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(CustomExceptionHandler()))
.build()
}
I'd like to set a header variable for a UUID that corresponds to the exception, for logging purposes. How would I do that?
Even better, is it possible to create a Spring Bean that puts the UUID in the header for all queries and mutations?
Thanks!
when you're using spring boot, there's two options:
you're using the spring boot graphql spqr starter (which brings it's own controller to handle all graphQL requests)
you're using plain graphql-spqr and have your own controller to handle GraphQL requests
In any case, you've got a few options:
Making your CustomExceptionHandler a Spring Bean and Autowiring HttpServletResponse
That would probably be the easiest way to go - and it would probably work in any case: You could simply make your CustomExceptionHandler a Spring bean and have it autowire the HttpServletRequest - in the handler method, you could then set it to whatever you would like it to be. Here's some dummy code in Java (sorry, I am not proficient enough in Kotlin):
#Component
class CustomExceptionHandler implements DataFetcherExceptionHandler {
private final HttpServletResponse response;
public CustomExceptionHandler(HttpServletResponse response) {
this.response = response;
}
#Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
response.setHeader("X-Request-ID", UUID.randomUUID().toString());
// ... your actual error handling code
}
}
This is going to work because spring will realise that HttpServletRequest differs for each request. It will therefore inject a dynamic proxy into your error handler that will point to the actual HttpServletResponse instance for every request.
I would argue, that it's not the most elegant way, but it will certainly solve your problem.
for the graphql-spqr spring boot starter
There's a default controller implementation that is used in projects using this starter. That controller will handle every graphql request that you receive. You can customise it, by implementing your own GraphQLExecutor and making it a spring bean. That executor is responsible to call the GraphQL engine, pass the parameters in and output the response. Here's the default implementation, that you might want to base your work on.
Similarly to the previous solution, you could autowire the HttpServletResponse in that class and set a HTTP Response header.
That solution would allow you to decide, if you want to set a request id in all cases, or just in specific error cases. (graphql.execute returns an object from which you can get the information if and what errors existed)
when using graphql-spqr without the spring boot starter
Locate your GraphQL controller, add an argument to that method of type HttpServletRequest - and then add headers to that as you prefer (see previous section on some more specific suggestions)
In our REST-API we need to be multi-tenant capable. For achiving this all rest controllers subclass a common REST controller which defines a request mapping prefix and exposes a model attribute as follows
#RequestMapping(path = "/{tenantKey}/api")
public class ApiController {
#ModelAttribute
public Tenant getTenant(#PathVariable("tenantKey") String tenantKey) {
return repository.findByTenantKey(tenantKey);
}
}
Derived controllers make use of the model attributes in their request mapping methods:
#RestController
public class FooController extends ApiController {
#RequestMapping(value = "/foo", method = GET)
public List<Foo> getFoo(#ApiIgnore #ModelAttribute Tenant tenant) {
return service.getFoos(tenant);
}
}
This endpoint gets well documented in the swagger-ui. I get an endpoint documented with a GET mapping for path /{tenantKey}/api/foo.
My issue is, that the {tenantKey} path variable is not documented in swagger-ui as parameter. The parameters section in swagger is not rendered at all. If I add a String parameter to controller method, annotating it with #PathVariable("tenantKey) everything is fine, but I don't want a tenantKey parameter in my controller method, since the resolved tenant is already available as model attribute.
So my question is: Is there a way do get the #PathVariable from the #ModelAttriute annotated method in ApiController documented within swagger-ui in this setup?
Project-Setup is
Spring-Boot (1.4.2)
springfox-swagger2 (2.6.1)
springfox-swagger-ui (2.6.1)
This is certainly possible. Model attributes on methods are not supported currently. Instead, you could take the following approach.
Mark the getTenant method with an #ApiIgnore (not sure if it gets treated as a request mapping.)
In your docket you can add tenantKey global path variable (to all end points). Since this is a multi-tenant app it's assuming this applies to all endpoints.
I want to have rest controller in Spring Boot to handle all requests like this: "/{arg}", EXCEPT "/sitemap.xml". How can I achieve that?
You could specify your request mapping on the controller level via regex and exclude some resources (e.g. 'excludeResourceA' and 'excludeResourceB') with:
#RestController
#RequestMapping(value = "/{arg:(?!sitemap.xml|excludeResourceA|excludeResourceB).*$}")
public class YourRestController {
// your implementation
}
Of course you can also specify the request mapping on the method level with the same regex relative to your controller path matching and you can pass the argument with #PathVariable("arg") String arg in your method signature to your method body if you need it.
I am trying to build RESTful web service by using spring 4.0
I have a controller:
#Controller
#RequestMapping("test")
public class Controller{
#RequestMapping("fetch",method=RequestMethod.GET)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public ResultResponse fetchController(ResultRequest req){
if((req.getName).equals("John"))
return new ResultResponse(100);
else
return new ResultResponse(0);
}
}
and my ResultRequest.class
public class ResultRequest{
private String name;
//getter,setter
}
If I hit the url to //host//contextPath/test/fetch?name=John
the controller will return the object ResultResponse(100)
my question is, there no #RequestParam or other annotation in the request parameter,
how does the spring controller know to set the query parameter "name" as the property of wrapper class
ResultRequest ?
Thanks
Spring uses implementations of an interface called HandlerMethodArgumentResolver for resolving arguments to pass to handler methods, ie. methods annotated with #RequestMapping.
One of these is a ModelAttributeMethodProcessor. Its javadoc states
Resolves method arguments annotated with #ModelAttribute and handles
return values from methods annotated with #ModelAttribute.
Model attributes are obtained from the model or if not found possibly
created with a default constructor if it is available. Once created,
the attributed is populated with request data via data binding and
also validation may be applied if the argument is annotated with
#javax.validation.Valid.
When this handler is created with annotationNotRequired=true, any
non-simple type argument and return value is regarded as a model
attribute with or without the presence of an #ModelAttribute.
Spring registers two objects of this type. One to handle parameters annotated with #ModelAttribute and one to handle ones without.
Further reading:
Form submit in Spring MVC 3 - explanation
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments
I have an existing application in Sping 3.0 that uses ControllerClassNameHandlerMapping to map Controller and methods such as:
StartController.class is mapped to http://127.0.0.1/app/start/*
then
StartController.class has a method called init() that is mapped to http://127.0.0.1/app/start/init.html
Here is my configuration:
#Bean
public ControllerClassNameHandlerMapping classNameControllerMappings() {
return new ControllerClassNameHandlerMapping() {{
setCaseSensitive(true);
setDefaultHandler(new UrlFilenameViewController());
setInterceptors(new Object[]
{callProgressionInterceptorHandler(),
callSessionInterceptorHandler(),
localeChangeInterceptor()});
}};
}
Most of my controllers have 5-15 Request Mapped methods in each controller.
But when I upgrade to Spring 3.1+, the Request Mapping becomes ambiguous for each controller and is not mapped correctly.
I have read that one solution is to explicitely add the mthod name:
#RequestMapping(method = RequestMethod.GET)
Will now be:
#RequestMapping(method = RequestMethod.GET, value = "init")
I really do not want to manually add #RequestMapping value to 100+ methods if I dont have to.
Can anyone help with a better solution?
Here is the error I keep getting:
47672 [btpool0-1] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'addressConfirmationController' bean method
public void com.comcast.ivr.d2.web.controllers.AddressConfirmationController.houseNumber_rc(org.springframework.ui.ModelMap)
to {[],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'addressConfirmationController' bean method
I also added setOrder(1); to ControllerClassNameHandlerMapping and still get this error.
UPDATE:
I saw on http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html the following excerpt:
Prior to Spring 3.1, type and method-level request mappings were examined in two separate stages -- a controller was selected first by the DefaultAnnotationHandlerMapping and the actual method to invoke was narrowed down second by the AnnotationMethodHandlerAdapter.
With the new support classes in Spring 3.1, the RequestMappingHandlerMapping is the only place where a decision is made about which method should process the request. Think of controller methods as a collection of unique endpoints with mappings for each method derived from type and method-level #RequestMapping information.
Does this mean I cannot keep the same #RequestMapping without adding mapping details in the #RequestMapping as I did <3.1 ?
I have hundreds of methods that would need to be modified in order for this to happen... :-(