I am working with Spring MVC controller. I have one of my controller as DataController.
I am thinking to add HttpServletRequest as injectable at the top of DataController class using #Inject.
#Controller
public class DataController {
#Inject
HttpServletRequest request;
// .. some code here
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow) {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
}
So my question is - Is this the right way to use #Inject? I have never used #Inject before so trying to learn whether the way I am doing it is right or not? Since everytime, who is making call to processTask method, I need to grab its ipAddress whoever is calling that processTask method.
In terms of acquiring HttpServletRequest: semantically speaking, it is definitely wrong.
Reason: HttpServletRequest is an object that is created only when users send requests and is destroyed once the requested user action is completed. You simply can store it that way (from syntax angle) but you shouldn't (from semantic angle). You need to realize that the way how web application works is not exactly same as a desktop application (and don't observe them from the same angle).
Suggestion:
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow, HttpServletRequest request) {...}
In this way you will get the corresponding request each time the processTask method is called. (HttpServletRequest object is injected by #RequestMapping.)
(If you would like to preserve something through out a session, consider use a bean that is
Suggestion: #Inject private UserService userService;
(assume we have a class registered called UserService.)
You cannot "inject" the HttpServletRequest the only way to use it as far as I know is to added as a method member. Like this:
#Controller
public class DataController {
// .. some code here
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow,HttpServletRequest request) {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
}
look also at Spring MVC #AutoWired response not working
Related
I'm interested in overloading an #PostMapping of say, /digital/testCase, with two separate signatures, that use #RequestBody annotations to pass data, rather than #RequestParams. This is important, because all of the other questions on StackOverflow pertain to the latter. Currently, Spring Boot crashes when I try overload a function.
For those interested in seeing the code, it would look something like this
package com.example.test;
import *
#RestController
#RequestMapping(path = "/digital", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class TestController {
private static final Logger LOG = LoggerFactory.getLogger(TestController.class);
#Autowired
private testDomain testDomain;
#PostMapping("/testCase")
public ResponseEntity<Object> testCase(#RequestBody InitiateTestCase initiateTestCase,
HttpServletRequest request) {
//
...some code here
//
}
#PostMapping("/testCase")
public ResponseEntity<Object> testCase(#RequestBody InitiateTestCase[] initiateTestCases,
HttpServletRequest request) {
//
...some code here
//
}
}
You can try to add some specific params/headers to PostMapping annotation so Spring will be able to map it properly. Another way of solving such problem is to abstract your pojo used as RequestBody and not overload methods.
I have a SB service that is being used to send email. I wanted to use that in my existing application , how can I do that? I am thinking to create a controller that handles incoming HttpRequest and HttpResponse. But still no idea on how my existing application will invoke it. I need some high level overview too on how exactly SB application will run independently with other application.
P.S.- there is no UI interface for the email service so i wont be mapping url like we do in controllers generally.
Here is my sample email service:
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
I created a controller like this earlier to test this out:
#RestController
public class CourseController {
#Autowired
private EmailService emailService;
#RequestMapping(value = "/sendEmail", method = RequestMethod.POST)
public void sendEmail() {
emailService.sendMessage("abc#gmail.com","{client#gmail.com}", "testSubject",new Context);
}
Context has some business data.
I have a jsp that I am using and posting my form through which it is mapping. It all works fine.
But now I want to integrate this with my existing application (its on struts 1)so there wont be any uri to map. There must be some kind of HttpRequest need to be created from the invoking application and my controller should be handling it. How can I achieve this?
You have already this service implemented? Then you need a RestController class that mapps the uri of your choice. In this class you need to inject the service class that realizes your email sending method. Is this class annotated with #Service? Quite difficult to explain without seeing your code. Here an example for a REST-Interface:
#RestController
#RequestMapping("/api/v1/email")
public class RestClass {
private EmailService emailService;
#Autowired
public RestClass(EmailService emailService){
this.emailService = emailService;
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> sendEmail(#RequestBody EmailDTO emailDTO){
String emailAdress = emailDTO.getEmail();
this.emailService.sendEmail(emailAdress);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
So in this case the emailService would be the class that has the method that sends your email. This class should be annotated with #Service. Hope that helps.
Here your existing class:
#Service
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
And in case the injection doesn't work you have to annotate your application class with #ComponentScan({"com.foo.dal.","com.foo.notification."}) replace this packages simply with the package of your service and resource class.
I am not sure about the problem. If I am right that you need to call a rest service from your application. In this case it is lot easier and convenient to use Spring's RestTemplate link
You can get some overview here
I did Google a lot to find my problem but I couldn't and sorry If this question already on the stack overflow because I have not find it.
First let take a look into the code
#Controller
public class Controller1 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
}
public int calculation(int i){
//Some Calcucation
return i;
}
}
and second controller is
#Controller
public class Controller2 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
//In this I want to call the calculation(1) method of controller1.
}
}
My question is that is there any way to call the method of calculation() of controler1 in to controller2. But remember I don't want to make method static in controller1.Is there anyway to call it without make it static?
Thanks
Yasir
You should create service bean for example in configuration file (or use # one of the annotaions) and inject it into controller. For example ()
#Configuration
public class MyConfig {
#Bean
public MyService myService(){
return new MyService();
}
}
#Controller
public class Controller1 {
#Autowire
private MyService myService;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
#Controller
public class Controller2 {
#Autowire
private MyBean myBean;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
Your controllers should not call each other. If there is a logic which needs to be used by both controllers, it is much better to put that into separate bean, which will be used by both controllers. Then you can simply inject that bean to whicheveer controller neccessary. Try not to put any business logic to controllers, try tu put it to specialized class instead which will be web independent if possible and will accept web agnostic business data as user email, account number etc. No http request or response. This way your class with actual logic is reusable and can be unit tested much more easily. Also, if there is state, it should be contained in your classes outside controllers. Controllers should be stateless and not contail any state at all.
When using MVC pattern and you are deciding where to put your logic, you should separate business logic into model and into controllers you should put only logic regarding user interaction, as explained in this stack overflow post.
I have used the Spring MVC. I set the Session Attribute value like
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String initHome(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
return "homeMenu";
}
It is working fine if i click the home menu url(/home). but if i did not go the
home means it says error as 'session attribute clientObject is required'
so i decided to set sessionattibutes in constructor of controller
#Autowired
public MyController(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
}
it also says error
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myController'
I tried to set using the RequestMapping also like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void initController(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
}
this method is also not called intially
my cointroller look like
#RequestMapping("/sample")
public class MyController {
..
..
is it possible to set the sessionAttribute value in the constructor of controller? or any other way to set the session Attribute initially?
Thanks in advance for your help.
Assuming your createDefaultClient is in the controller add a #ModelAttribute annotation to it.
#ModelAttribute("clientObject")
public ClientObject createDefaultClient() { ... }
This method will be called before any request handling method (as explained in the reference guide)
If you combine that with a #SessionAttribute annotation on your class (which you might already have). You should be able to achieve what you want.
In your request handling methods (methods annotated with #RequestMapping) you can now simply inject the client object as a method argument.
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String initHome(#ModelAttribute("clientObject") ClientObject clientObject) {
// Do something with the clientObject
return "homeMenu";
}
This will only work consistenly within the same controller, so if you need the ClientObject to be used somewhere else (another controller for instance), this isn't going to work (nor is #SessionAttributes designed for that).
In Spring MVC, it is easy to bind request parameter to method paramaters handling the request. I just use #RequestParameter("name"). But can I do the same with request attribute? Currently, when I want to access request attribute, I have to do following:
MyClass obj = (MyClass) request.getAttribute("attr_name");
But I really would like to use something like this instead:
#RequestAttribute("attr_name") MyClass obj
Unfortunately, it doesn't work this way. Can I somehow extend Spring functionality and add my own "binders"?
EDIT (what I'm trying to achieve): I store currently logged user inside request attribute. So whenever I want to access currently logged user (which is pretty much inside every method), I have to write this extra line user = (User) request.getAttribute("user");. I would like to make it as short as possible, preferably inject it as a method parameter. Or if you know another way how to pass something across interceptors and controllers, I would be happy to hear it.
Well, I finally understood a little bit how models work and what is #ModelAttribute for. Here is my solution.
#Controller
class MyController
{
#ModelAttribute("user")
public User getUser(HttpServletRequest request)
{
return (User) request.getAttribute("user");
}
#RequestMapping(value = "someurl", method = RequestMethod.GET)
public String HandleSomeUrl(#ModelAttribute("user") User user)
{
// ... do some stuff
}
}
The getUser() method marked with #ModelAttribute annotation will automatically populate all User user parameters marked with #ModelAttribute. So when the HandleSomeUrl method is called, the call looks something like MyController.HandleSomeUrl(MyController.getUser(request)). At least this is how I imagine it. Cool thing is that user is also accessible from the JSP view without any further effort.
This solves exactly my problem however I do have further questions. Is there a common place where I can put those #ModelAttribute methods so they were common for all my controllers? Can I somehow add model attribute from the inside of the preHandle() method of an Interceptor?
Use (as of Spring 4.3) #RequestAttribute:
#RequestMapping(value = "someurl", method = RequestMethod.GET)
public String handleSomeUrl(#RequestAttribute User user) {
// ... do some stuff
}
or if the request attribute name does not match the method parameter name:
#RequestMapping(value = "someurl", method = RequestMethod.GET)
public String handleSomeUrl(#RequestAttribute(name="userAttributeName") User user) {
// ... do some stuff
}
I think what you are looking for is:
#ModelAttribute("attr_name") MyClass obj
You can use that in the parameters for a method in your controller.
Here is a link a to question with details on it What is #ModelAttribute in Spring MVC?
That question links to the Spring Documentation with some examples of using it too. You can see that here
Update
I'm not sure how you are setting up your pages, but you can add the user as a Model Attribute a couple different ways. I setup a simple example below here.
#RequestMapping(value = "/account", method = RequestMethod.GET)
public ModelAndView displayAccountPage() {
User user = new User(); //most likely you've done some kind of login step this is just for simplicity
return new ModelAndView("account", "user", user); //return view, model attribute name, model attribute
}
Then when the user submits a request, Spring will bind the user attribute to the User object in the method parameters.
#RequestMapping(value = "/account/delivery", method = RequestMethod.POST)
public ModelAndView updateDeliverySchedule(#ModelAttribute("user") User user) {
user = accountService.updateDeliverySchedule(user); //do something with the user
return new ModelAndView("account", "user", user);
}
Not the most elegant, but works at least...
#Controller
public class YourController {
#RequestMapping("/xyz")
public ModelAndView handle(
#Value("#{request.getAttribute('key')}") SomeClass obj) {
...
return new ModelAndView(...);
}
}
Source : http://blog.crisp.se/tag/requestattribute
From spring 3.2 it can be done even nicer by using Springs ControllerAdvice annotation.
This then would allow you to have an advice which adds the #ModelAttributes in a separate class, which is then applied to all your controllers.
For completeness, it is also possible to actually make the #RequestAttribute("attr-name") as is.
(below modified from this article to suit our demands)
First, we have to define the annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface RequestAttribute {
String value();
}
Then we need a [WebArgumentResolver] to handle what needs to be done when the attribute is being bound
public class RequestAttributeWebArgumentResolver implements WebArgumentResolver {
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) throws Exception {
// Get the annotation
RequestAttribute requestAttributeAnnotation = methodParameter.getParameterAnnotation(RequestAttribute.class);
if(requestAttributeAnnotation != null) {
HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
return request.getAttribute(requestAttributeAnnotation.value);
}
return UNRESOLVED;
}
}
Now all we need is to add this customresolver to the config to resolve it:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolver">
<bean class="com.sergialmar.customresolver.web.support.CustomWebArgumentResolver"/>
</property>
</bean>
And we're done!
Yes, you can add your own 'binders' to the request attribute - see spring-mvc-3-showcase, or use #Peter Szanto's solution.
Alternatively, bind it as a ModelAttribute, as recommended in other answers.
As it's the logged-in user that you want to pass into your controller, you may want to consider Spring Security. Then you can just have the Principle injected into your method:
#RequestMapping("/xyz")
public String index(Principal principle) {
return "Hello, " + principle.getName() + "!";
}
In Spring WebMVC 4.x, it prefer implements HandlerMethodArgumentResolver
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(RequestAttribute.class) != null;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return webRequest.getAttribute(parameter.getParameterAnnotation(RequestAttribute.class).value(), NativeWebRequest.SCOPE_REQUEST);
}
}
Then register it in RequestMappingHandlerAdapter