i am using Spring 3.x for my MVC application without annotation. I want to get data only not view . I google it and found it is possible using #ResponseBody . but i dont want to use annotation. how can i tell spring it is only data not a view without annotation. my sample code given below .
public class ShowGraphController extends AbstractController {
private JdbcUserDao userDao;
public void setUserDao(JdbcUserDao userDao) {
this.userDao = userDao;
}
protected ModelAndView handleRequestInternal(HttpServletRequest request,HttpServletResponse responce) throws Exception {{
return new ModelAndView("want it retun as a data not a view name only");
}
}
I assume you mean JSON when you say data?
If you have to use ModelAndView style just handle the HttpServletResponse yourself and return null.
It is bit convoluted as with Spring 3, you should ideally be using ResponseBody annotation. Have a look at this class ResponseEntity , this may be useful for your purpose. Sample code from Spring doc :
#RequestMapping("/handle")
public ResponseEntity<String> handle() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new
ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}
the easiest way is to use #ResponseBody, but if you do not want to use Annotation, you can populate response yourself:
protected void handleRequestInternal(HttpServletRequest request,HttpServletResponse response) throws Exception {
response.getWriter().println("want it retun as a data not a view name only");
}
Related
I need to hide a specific API for requests coming form IP different to a specific one.
For instance this should work if I try to use it and my IP is 192.168.1.1, but not if my IP is 192.168.1.2.
#RequestMapping(value = "/test/{id}", method = RequestMethod.GET)
#ResponseBody
#IpRestricted
public void download(#PathVariable("id") String id) {
...
}
I read I can make it creating a specific annotation, the one I called "#IpRestricted" in this example, but than how can I proceed? There are better solution to this?
I then realized I can make it without using spring security.
I made an annotation like this:
#Retention(RetentionPolicy.RUNTIME)
public #interface IpRestricted {
}
Than I check the request IP address inside a HandlerInterceptor preHandle method:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod)handler;
if (method.getMethodAnnotation(IpRestricted.class)!=null) {
if (!request.getRemoteAddr().equals("192.168.1.1")) {
throw new UnauthorizedException("Ip not authorized");
}
}
}
[....]
}
And for the download method:
#RequestMapping(value = "/test/{id}", method = RequestMethod.GET)
#ResponseBody
#IpRestricted
public void download(#PathVariable("id") String id) {
...
}
That's it!
I think the best Spring solution available for this case is the hasIpAddress() method from Spring Security. There are many different ways to configure permissions to your services via Spring Security, and the IP-based solution is also implemented.
Here is a good example of how to set it up.
I am having a Spring controller with a Validator defined as:
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new MyValidator(myService));
}
And calling it:
public ResponseEntity<?> executeSomething(
#ApiParam(name = "monitorRequest", required = true, value = "") #Valid #RequestBody MonitorRequest monitorRequest,
HttpServletRequest request, HttpServletResponse response) throws RESTException
I need to add one more Validator for this controller that could be called from some specific methods of this controller. Is there any way to achieve this?
EDIT: I am handling the Error by:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<?> processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
ValidationErrorObj obj = processFieldErrors(fieldErrors);
ResponseEntity r = new ResponseEntity(obj, HttpStatus.BAD_REQUEST);
return r;
}
You can have more than one InitBinder method in a controller. It is controlled by the optional value parameter . For the javadoc of InitBinder : String[] value : The names of command/form attributes and/or request parameters that this init-binder method is supposed to apply to ... Specifying model attribute names or request parameter names here restricts the init-binder method to those specific attributes/parameters, with different init-binder methods typically applying to different groups of attributes or parameters.
Another way would be to explicely call a complementary Validator in specific methods.
BTW : I can't see any Errors or BindingResult in your controller method signature : where do you find whether errors occured ?
For those who are still trying to figure out how to solve this in 2017. I was facing similar issues while trying to implement 2 validators in my RestController. I followed the approach mentioned above by #Serge Ballasta.
I ended up making 2 Model each of linked to their specific Validators. The Controller methods look something like
#RequestMapping(value = "register", method = RequestMethod.POST)
public ResponseEntity<User> register(#Valid #RequestBody UserRegisterRequest userRegisterRequest) {
return null;
}
#RequestMapping(value = "test", method = RequestMethod.POST)
public ResponseEntity<?> test(#Valid #RequestBody TestRequest testRequest) {
return null;
}
and I created 2 initBinders to wire these validators in the controller like
#InitBinder("testRequest")
public void setupBinder(WebDataBinder binder) {
binder.addValidators(testValidator);
}
#InitBinder("userRegisterRequest")
public void setupBinder1(WebDataBinder binder) {
binder.addValidators(userRegistrationRequestValidator);
}
Please note that the #RequestBody attributes (userRegisterRequest , testRequest) had to be provided as values in the #InitBinder() annotations.
By the way the in my code I handle the bindingResult in a custom ExceptionHandler class which extends ResponseEntityExceptionHandler which gives me freedom to do custom handling of the response.
I have the following property that I need mapped to a post parameter in Spring. Is there an attribute I can use? It accepts application/x-www-form-urlencoded for string-based payloads, multipart/form-data for binary payloads. Other properties are mapping fine without underscores.
String deliveryAttemptId;
mapped to the post parameter
DELIVERY-ATTEMPT-ID
Controller
#Controller
#RequestMapping("/notifications")
public class NotificationController {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public void grade(EventNotificationRequest request, HttpServletResponse response) throws Exception {
}
Model
public class EventNotificationRequest {
String deliveryAttemptId;
I just made a work around for this Spring limitation. This also fixes case sensitivity issues with parameters. Sorry I am used to .NET and how easy binding is so it's frustrating to run into these Spring issues.
HttpServletRequest parameter lowecase
#RequestMapping(method = RequestMethod.POST, value = "/grade")
#ResponseBody
public void grade(HttpServletRequest request, HttpServletResponse response) throws Exception {
EventNotificationRequest notificationRequest = new LearningStudioEventNotificationRequest();
notificationRequest.setDeliveryAttemptId(getCaseInsensitiveParameter(request, "DELIVERY-ATTEMPT-ID"));
In prior versions of spring (3.0), it was possible to test your controllers via the correct urls using the RequestMappingHandlerAdapter and HandlerMapping objects in the ApplicationContext. However, in Spring 3.1, things have changed and the code I used to use to make this work no longer functions.
How do you test Spring controller urls in Spring 3.1? For example, I'd like to write code that looks like this:
ModelAndView modelAndView = handle("GET", "/businesses");
That way I'm testing my mappings in addition to the controller's action logic.
In particular, I am most interested in making sure that I can pass session attributes and have them correctly passed to my controller actions's #Valid annotation.
Is there any way to accomplish this with Spring 3.1?
This is the code I was using:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
final HandlerInterceptor[] interceptors = handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
}
protected ModelAndView handle(String method, String path, String queryString) throws Exception {
request.setMethod(method);
request.setRequestURI(path);
if(queryString != null) {
String[] parameters = queryString.split("&");
for(String parameter : parameters) {
String[] pair = parameter.split("=");
if(pair.length == 2) {
request.setParameter(pair[0], pair[1]);
} else {
request.setParameter(pair[0], "");
}
}
}
return handle(request, response);
}
protected ModelAndView handle(String method, String path, String attribute, Object object) throws Exception {
MockHttpSession session = new MockHttpSession();
session.setAttribute(attribute, object);
request.setSession(session);
return handle(method, path, null);
}
protected ModelAndView handle(String method, String path) throws Exception {
return handle(method, path, null);
}
protected void assertContentType(ModelAndView modelAndView, String contentType) {
assertEquals(contentType, modelAndView.getView().getContentType());
}
Let me instead recommend spring-test-mvc which is currently in 1.0.0M1 but is planned to be packaged with newer Spring MVC versions. It should be able to handle the cases that you are looking for quite easily, your test would end up looking like this:
xmlConfigSetup("classpath:/META-INF/spring/web/webmvc-config.xml")
.configureWebAppRootDir("src/main/webapp", false).build()
.perform(get("/businesses").param("name", "param1"))
.andExpect(status().isOk())
.andExpect(view().name("viewname"));
Your test does look appropriate for 3.1, so if you still want to continue with your approach can you point exactly what is not working - it sounds like normal requests are going through but the session attributes don't seem to be binding?
This is one of the test cases I used with Spring 3.1. Hope it can meet your requirement.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml" })
public class ControllerTest {
#Autowired
private RequestMappingHandlerAdapter handleAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Test
public void playerControllerTest() throws Exception{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setRequestURI("/players.show");
request.setMethod("GET");
Object handler = handlerMapping.getHandler(request).getHandler();
ModelAndView mav = handleAdapter.handle(request, response,handler);
ModelAndViewAssert.assertViewName(mav,"players");
}
}
Here is a very nice presentation which talks about testing Spring 3.1 classes and controller, following is an example:
#Test
public void testSave() {
Account account = new Account();
BindingResult result =
new BeanPropertyBindingResult(account, "account");
AccountManager mgr = createMock(AccountManager.class);
mgr.saveOrUpdate(account);
replay(mgr);
AccountController contrlr = new AccountController(mgr);
String view = contrlr.save(account, result);
assertEquals("redirect:accounts", view);
verify(mgr);
}
Hope that helps!
I am trying to convert controllers from the old inheritance framework to the new annotations.
Here's an existing controller:
public class SelectedTabController extends AbstractController {
private TabSelectionHelper tabSelectionHelper;
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
String param = request.getParameter("selectedTab");
if (param != null)
tabSelectionHelper.setSelectedTabTo(param);
return null;
}
public void setTabSelectionHelper(TabSelectionHelper tabSelectionHelper) {
this.tabSelectionHelper = tabSelectionHelper;
}
And after conversion I have this:
#Controller
public class SelectedTabController {
private TabSelectionHelper tabSelectionHelper;
#Autowired
public SelectedTabController(#Qualifier(value = "tabSelectionHelper") TabSelectionHelper tabSelectionHelper) {
this.tabSelectionHelper = tabSelectionHelper;
}
#RequestMapping("/selectedTab")
public void selectTab(String selectedTab, HttpServletResponse response) throws Exception {
//String param = request.getParameter("selectedTab");
if (selectedTab != null)
tabSelectionHelper.setSelectedTabTo(selectedTab);
}
}
This works but there is a (redundant) HttpServletResponse object in the selectTab paramter list. If I remove it, then the JQuery call says the server returns 500 and the call fails.
Any help?
The stacktrace shows:
javax.servlet.ServletException: Could not resolve view with name 'selectedTab' in servlet with name 'prodman'
So it is trying to find a view and failing. However, there is NO view to display as its a backend callby JQuery.
I guess by declaring the response object, Spring thinks I will write the response.
How can I prevent Spring from trying to resolve a view?
When you use void as your return type Spring will by default try to determine the view name from your method name, unless it thinks you're directly writing the response (which it does when you have a HttpServletResponse as a parameter). Have a look at section 15.3.2.3 of the Spring 3 docs.
You might want to try changing the return type to ModelAndView and return null and see what happens (I'm not certain you can get away with a null view with #RequestMapping as it's not something that I have ever tried)