I'm Adding 3 Categories using Post Request and for every Category, 3 respective Courses are added to the same. Finally I need to get an array of Categories containing list of courses.
But while performing Test Driven Development using Mockito, I', recieving a null Pointer Exception with status code 400.
Kindly help me in the same.
Test.java
#RunWith(value = MockitoJUnitRunner.class)
#WebMvcTest(Controller.class)
class Test {
#Autowired
MockMvc mvc;
#MockBean
service s;
#MockBean
Controller cont;
#MockBean
StatusResultMatchers sr;
#org.junit.jupiter.api.Test
void catTest() throws Exception {
Course c1=new Course(1,"Technology",100,50,7415);
Course c2=new Course(2,"Technology",100,50,7415);
Course c3=new Course(3,"Technology",100,50,7415);
Course[] c= {c1,c2,c3};
List<Course> cl=new ArrayList<Course>();
cl.add(c1);
cl.add(c2);
cl.add(c3);
Category ca=new Category(7415,"java","very gud", cl);
when(s.getAllContents()).thenReturn(cl);
mvc.perform(get("/getcourse")).andExpect(sr.is2xxSuccessful()).andReturn();
}
}
Controller.java
#RestController
public class Controller {
#Autowired
private service cs;
#RequestMapping(value="/addcat", method=RequestMethod.POST)
public void addcat(#RequestBody Category[] categ) throws Exception{
for(int i=0;i<3;i++)
cs.cat[i]=categ[i];
}
#RequestMapping(value="/addcour", method=RequestMethod.POST)
public void addcour(#RequestBody Course[] cour) {
for(Category c:cs.cat) {
cs.co=null;
for(int i=0;i<3;i++) {
if(cour[i].getCategoryId()==c.getCategoryId()) {
cs.co.add(cour[i]);
c.setCourseList(cs.co);
}
}
}
}
#RequestMapping(value="/getcat", method=RequestMethod.GET)
public #ResponseBody Category[] getcat(){
return cs.getAllCategories();
}
#RequestMapping(value="/getcourse", method=RequestMethod.GET)
public #ResponseBody List<Course> getcourse(#RequestBody Map<Category,List<Course> > cour){
return cs.getAllContents();
}
}
Service.java
#Service
public class service {
public List<Course> co=new ArrayList<Course>();
public Category[] cat=new Category[3];
public List<Course> getAllContents() {
return co;
}
public Category[] getAllCategories() {
return cat;
}
}
Test Output
MockHttpServletRequest:
HTTP Method = GET
Request URI = /getcourse
Parameters = {}
Headers = []
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.example.demo.controller.Controller$MockitoMock$138133307
Method = com.example.demo.controller.Controller$MockitoMock$138133307#getcourse(Map)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
2020-06-03 17:38:59.573 INFO 26360 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
I have updated my code. Now, NullPOinterException is not there. But I'm getting an empty list "ActualCourse".
Please find d code below:
Test.java
#RunWith(value = MockitoJUnitRunner.class)
#WebMvcTest(Controller.class)
class Test {
#Autowired
MockMvc mvc;
#MockBean
service testService;
#MockBean
Controller targetController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#org.junit.jupiter.api.Test
void catTest() throws Exception {
// Mock Data
Course c1=new Course(1,"Technology",100,50,7415);
Course c2=new Course(2,"Technology",100,50,7415);
Course c3=new Course(3,"Technology",100,50,7415);
List<Course> expectedCourse=new ArrayList<Course>();
expectedCourse.add(c1);
expectedCourse.add(c2);
expectedCourse.add(c3);
Category expectedCategory=new Category(7415,"java","very gud", expectedCourse);
when(testService.addcour()).thenReturn(expectedCourse);
List<Course> actualCourse = targetController.addcour();
assertEquals(expectedCourse,actualCourse);
}
Controller.java
#RestController
public class Controller {
#Autowired
private service cs;
#RequestMapping(value="/addcat", method=RequestMethod.POST)
public List<Category> addcat(#RequestBody Category[] categ) throws Exception{
return cs.addcat(categ);
}
#RequestMapping(value="/addcour", method=RequestMethod.POST)
public List<Course> addcour() {
return cs.addcour();
}
#RequestMapping(value="/getcat", method=RequestMethod.GET)
public #ResponseBody List<Category> getcat(){
return cs.getAllCategories();
}
#RequestMapping(value="/getcourse", method=RequestMethod.GET)
public #ResponseBody List<Course> getcourse(){
return cs.getAllCourses();
}
}
Service.java
#Service
public class service {
public List<Category> cat=new ArrayList<Category>();
public List<Course> c=new ArrayList<Course>();
List<Course> empty=new ArrayList<Course>();
List<Category> category=new ArrayList<Category>();
public List<Category> addcat(Category[] categ) {
int flagcat=0,flagcourse=0;
Category c1=new Category(7415,"Technology","Java",empty);
Category c2=new Category(2,"Technology","Java",empty);
Category c3=new Category(7314,"Technology","Java",empty);
Category c4=new Category(4,"Technology","Java",empty);
Category c5=new Category(8415,"Technology","Java",empty);
Category c6=new Category(6,"Technology","Java",empty);
category=Arrays.asList(c1,c2,c3,c4,c5,c6);
Iterator icat=category.iterator();
Iterator icourse=c.iterator();
Object ncourse=new Object();
Object ncat=new Object();
while(icat.hasNext()) {
List<Course> co=new ArrayList<Course>();
flagcat++;
flagcourse=0;
ncat=icat.next();
while(icourse.hasNext()) {
ncourse=icourse.next();
if(((Category) ncourse).getCategoryId()==((Category) ncat).getCategoryId()) {
flagcourse++;
co.add((Course) ncourse);
if(flagcourse==3) {
((Category) ncat).setCourseList(co);
break;
}
}
}
cat.add((Category) icat);
if(flagcat==3) {
break;
}
}
return cat;
}
public List<Course> addcour() {
Course c1=new Course(1,"Technology",100,50,7415);
Course c2=new Course(2,"Technology",100,50,7415);
Course c3=new Course(3,"Technology",100,50,7415);
Course c4=new Course(4,"Technology",100,50,7314);
Course c5=new Course(5,"Technology",100,50,7314);
Course c6=new Course(6,"Technology",100,50,7314);
Course c7=new Course(7,"Technology",100,50,8415);
Course c8=new Course(8,"Technology",100,50,8415);
Course c9=new Course(9,"Technology",100,50,8415);
Course c10=new Course(10,"Technology",100,50,8415);
List<Course> c=Arrays.asList(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10);
return c;
}
public List<Course> getAllCourses() {
return c;
}
public List<Category> getAllCategories() {
return cat;
}
}
Output
org.opentest4j.AssertionFailedError: expected: <[com.example.demo.Course#6ecc02bb, com.example.demo.Course#31973858, com.example.demo.Course#65514add]> but was: <[]>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124)
at com.example.demo.test.Test.catTest(Test.java:75)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
It seems you have some design issues in your code. In your controller, the getcourse method is receiving a request body Map<Category,List<Course> > cour, but the HTTP method is a GET, you're not supposed to send data in a GET method (check this answer and this for more context).
On the other side, your getcourse is not using the cour parameter, so refactor this method as:
#RequestMapping(value="/getcourse", method=RequestMethod.GET)
public #ResponseBody List<Course> getcourse(){
return cs.getAllContents();
}
Let me know if it works.
This work fine
#RequestMapping(value = "/users/emailfrom2")
public String displayLogin3(Model model, HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession(true);
String gsSessionID = (String) session.getAttribute("GSSessionID");
System.out.println("Session ===>>>> " + gsSessionID);
logger.debug("displayLogin3()");
return "users/test1";
}
but i want like this.like a global value.
public class UserController {
String gsSessionID = (String) session.getAttribute("GSSessionID");
#RequestMapping(value = "/users/emailfrom2")
public String displayLogin3(Model model, HttpServletRequest request, HttpServletResponse response) {
System.out.println("Session ===>>>> " + gsSessionID);
logger.debug("displayLogin3()");
return "users/test1";
}
#RequestMapping(value = "/users/emailfrom5")
public String displayLogin5(Model model, HttpServletRequest request, HttpServletResponse response) {
String sessionId = getSessionProperty(request, "GSSessionID");
System.out.println("Session ===>>>> " + sessionId);
logger.debug("displayLogin3()");
return "users/test1";
}
}
#RequestMapping(value = "/users/emailfrom6")
public String displayLogin5(Model model, HttpServletRequest request, HttpServletResponse response) {
String sessionId = getSessionProperty(request, "GSSessionID");
System.out.println("Session ===>>>> " + sessionId);
logger.debug("displayLogin3()");
return "users/test1";
}
i just want to write String sessionId = getSessionProperty(request, "GSSessionID"); only one time in controller class not twice or thrice or more.
Any help will be appreciated..
Use something like this:
public class UserController extends AbstractController {
#RequestMapping(value = "/users/emailfrom2")
public String displayLogin3(Model model, HttpServletRequest request, HttpServletResponse response) {
String sessionId = getSessionID(request)
System.out.println("Session ===>>>> " + sessionId);
logger.debug("displayLogin3()");
return "users/test1";
}
}
public abstract class AbstractController {
public String getSessionID(HttpServletRequest request){
HttpSession session = request.getSession(true);
return (String) session.getAttribute("GSSessionID");
}
}
UPDATE:
If as you said you can need different values from session you should modify this method:
public abstract class AbstractController {
protected String gSSessionID;
protected String gSPresentDate;
#PreProcess
public void loadSessionAttributes(#SessionAttribute(name="GSSessionID") String gSSessionID,
#SessionAttribute(name="GSPresentDate") String gSPresentDate){
this.gSSessionID = gSSessionID;
this.gSPresentDate = gSPresentDate;
//Some other session attributes
}
}
public class UserController extends AbstractController {
#RequestMapping(value = "/users/emailfrom2")
public String displayLogin3(Model model, HttpServletRequest request, HttpServletResponse response) {
System.out.println("Session ===>>>> " + gSSessionID);
logger.debug("displayLogin3()");
return "users/test1";
}
}
I have some problems with my webapp. Here is my code:
Config:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"app.controllers", "app.service"})
public class MainStConfig extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/",
"classpath:/webjars/",
"classpath:/tempplates/"
};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
#Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setLoginUrl("/login.html");
shiroFilter.setSuccessUrl("/index.html");
shiroFilter.setUnauthorizedUrl("/index.html?error");
Map<String, String> filterChain = new HashMap<>();
filterChain.put("/", "anon");
filterChain.put("/login", "authcBasic");
filterChain.put("/logout", "logout");
filterChain.put("/admin/**", "authc,roles[ADMIN]");
filterChain.put("/student/**", "authc,roles[STUDENT]");
filterChain.put("/teacher/**", "authc,roles[TEACHER]");
//filterChain.put("/student/**", "authc,roles[STUDENT]");
//filterChain.put("/teacher/**", "roles,roles[TEACHER]");
shiroFilter.setFilterChainDefinitionMap(filterChain);
shiroFilter.setSecurityManager(securityManager());
Map<String, Filter> filters = new HashMap<>();
filters.put("anon", new AnonymousFilter());
filters.put("authc", new FormAuthenticationFilter());
filters.put("logout", new LogoutFilter());
filters.put("roles", new RolesAuthorizationFilter());
filters.put("user", new UserFilter());
shiroFilter.setFilters(filters);
return shiroFilter;
}
#Bean
public org.apache.shiro.mgt.SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
return securityManager;
}
#Bean(name = "userRealm")
#DependsOn("lifecycleBeanPostProcessor")
public UserRealm userRealm() {
return new UserRealm();
}
#Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
IndexController:
#Controller
#RequestMapping("/")
public class IndexController {
#RequestMapping(value = "/", method = RequestMethod.GET)
String start() {
return "index";
}
#RequestMapping(value = "/index", method = RequestMethod.GET)
String index() {
return "index";
}
#RequestMapping("/login")
String login() {
return "login";
}
}
LoginController
#Controller
public class LoginController {
private Session session;
#ModelAttribute("userR")
public User getUser() {
return new User();
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
return "login";
}
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logoutt() {
return "redirect:/index";
}
#RequestMapping(value = "/admin/index", method = RequestMethod.GET)
public String admin() {
return "admin/index";
}
#RequestMapping(value = "/student/index", method = RequestMethod.GET)
public String student() {
return "student/index";
}
#RequestMapping(value = "/teacher/index", method = RequestMethod.GET)
public String teacher() {
return "teacher/index";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(Model model, #ModelAttribute("userR") User user, RedirectAttributes redirectAttrs, SessionStatus status) {
Subject currentUser = SecurityUtils.getSubject();
model.addAttribute("login", user.getLogin());
if (StringUtils.hasText(user.getLogin()) && StringUtils.hasText(user.getPassword())) {
try {
UsernamePasswordToken token = new UsernamePasswordToken(user.getLogin(), user.getPassword());
token.setRememberMe(true);
currentUser.login(token);
session = currentUser.getSession(false);
if(currentUser.hasRole("ADMIN")) {
status.setComplete();
return "redirect:/admin/index";
}
if(currentUser.hasRole("STUDENT")) {
status.setComplete();
return "redirect:/student/index";
}
if(currentUser.hasRole("TEACHER")) {
status.setComplete();
return "redirect:/teacher/index";
}
} catch (Exception e) {
return "login";
}
return "redirect:index";
} else {
return "login";
}
}
#RequestMapping(value = "/logout", method = RequestMethod.POST)/*#RequestMapping(value = "/logout", method = RequestMethod.POST)*/
public String logout() {
Subject currentUser = SecurityUtils.getSubject();
try {
session.stop();
currentUser.logout();
return "redirect:/index";
} catch (Exception e) {
return "redirect:/index";
}
}
}
So, with this code always when I'm starting index page in my console I can see pages without layout with css - white pages without bootstrap but when i change this method i LoginController:
#RequestMapping(method = RequestMethod.POST)
public String logout() {
Subject currentUser = SecurityUtils.getSubject();
try {
session.stop();
currentUser.logout();
return "redirect:/index";
} catch (Exception e) {
return "redirect:/index";
}
}
to:
#RequestMapping(value = "/logout", method = RequestMethod.POST)
then everything works, I can see all colors etc but here is problem now, When I try to log out, for example from /admin/index then I'm redirecting to /admin/logout with Whitable error. Should be redirecting to index page like without "value = "/logout"" because then it works.
I use fot log out button:
<form th:action="#{logout}" method="POST">
<input type="submit" class="btn btn-info text-center center-block"
value="Wyloguj" />
</form>
And when I'm not using this "value = "logout"" then after log out in console I can see this warning:
2017-01-06 19:40:56.135 WARN 3400 --- [nio-8080-exec-6] o.s.web.servlet.PageNotFound : Request method 'GET' not supported
What am I doing wrong?
I have the following controller logic. However, if I navigate to a non-existing page (e.g. /random-page), I end up with a TemplateInputException. How can I catch this and go to the 404 page?
#RequestMapping(value = { "{path:(?!resources|error).*$}", "{path:(?!resources|error).*$}/**" }, headers = "Accept=text/html")
public String index(final HttpServletRequest request) {
try {
String path = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
return path.split("/")[1];
} catch (Exception e) {
log.error("Failed to render the page. {}",e);
return "error/general";
}
}
Thymeleaf seems to be ignoring the ExceptionHandler:
#ExceptionHandler(Exception.class)
public ModelAndView handleAllException(Exception ex) {
ModelAndView model = new ModelAndView("error/generic_error");
model.addObject("errMsg", "this is Exception.class");
return model;
}
My workaround this problem for spring-boot (exception is view with message param):
#Controller
public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController {
private static final String ERROR_PATH = "/error";
#Autowired
private ErrorAttributes errorAttributes;
#Override
public String getErrorPath() {
return ERROR_PATH;
}
#RequestMapping(ERROR_PATH)
public String error(HttpServletRequest request, Model model) {
Map<String, Object> errorMap = errorAttributes.getErrorAttributes(new ServletRequestAttributes(request), false);
String exception = (String) errorMap.get("exception");
if (exception != null && exception.contains("TemplateInputException")) {
errorMap.put("message", "Неверный запрос");
}
model.addAllAttributes(errorMap);
return "exception";
}
}
I'm trying to implement this Jsonp Filter.
However, whenever I call my url /server/api.jsonp?callback=something it cuts of the last part of the response (ContentLength has the length of the original response output. In this case that would be 15 since {test: 'blaat'} is my original response output - see Controller).atleast this is what I assume since the output I get is test({test: 'bl. Does someone know how to get the filter to work and send the correct response back?
(I even try to force the Content-length - see JsonpCallbackFilter)
Filter:
public class JsonpCallbackFilter implements Filter {
protected final Log logger = LogFactory.getLog(JsonpCallbackFilter.class);
#Override
public void init(FilterConfig fConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
Map<String, String[]> params = httpRequest.getParameterMap();
if (params.containsKey("callback")) {
if (logger.isDebugEnabled()) {
logger.debug("Wrapping response with JSONP callback: " + params.get("callback")[0]);
}
OutputStream out = httpResponse.getOutputStream();
GenericResponseWrapper wrapper = new GenericResponseWrapper(httpResponse);
chain.doFilter(request, wrapper);
byte[] callBack = (params.get("callback")[0] + "(").getBytes();
byte[] callBackEnd = (");").getBytes();
byte[] jsonpResponse = new byte[callBack.length + wrapper.getData().length + callBackEnd.length];
System.arraycopy(callBack, 0, jsonpResponse, 0, callBack.length);
System.arraycopy(wrapper.getData(), 0, jsonpResponse, callBack.length, wrapper.getData().length);
System.arraycopy(callBackEnd, 0, jsonpResponse, callBack.length + wrapper.getData().length, callBackEnd.length);
logger.debug(new String(jsonpResponse));
logger.debug("Length: " + jsonpResponse.length);
out.write(jsonpResponse);
wrapper.setContentType("text/javascript;charset=UTF-8");
wrapper.setContentLength(jsonpResponse.length);
} else {
if (logger.isDebugEnabled()) {
logger.debug("No callback found, resort to default json request!");
}
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
}
}
Generic Response Wrapper:
public class GenericResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream output;
private FilterServletOutputStream filterStream;
private PrintWriter printWriter;
private int contentLength;
private String contentType;
public GenericResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteArrayOutputStream();
filterStream = new FilterServletOutputStream(output);
printWriter = new PrintWriter(output, true);
}
public byte[] getData() {
printWriter.close();
return output.toByteArray();
}
#Override
public ServletOutputStream getOutputStream() {
return filterStream;
}
#Override
public PrintWriter getWriter() {
return printWriter;
}
public int getContentLength() {
return contentLength;
}
#Override
public void setContentLength(int length) {
this.contentLength = length;
super.setContentLength(length);
}
#Override
public String getContentType() {
return contentType;
}
#Override
public void setContentType(String contentType) {
this.contentType = contentType;
super.setContentType(contentType);
}
}
Custom Filter outputstream:
public class FilterServletOutputStream extends ServletOutputStream {
private DataOutputStream stream;
public FilterServletOutputStream(OutputStream output) {
stream = new DataOutputStream(output);
}
#Override
public void write(int b) throws IOException {
stream.write(b);
}
#Override
public void write(byte[] b) throws IOException {
stream.write(b);
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
stream.write(b, off, len);
}
}
web.xml filter mapping:
<filter>
<filter-name>jsonpCallbackFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>jsonpCallbackFilter</filter-name>
<url-pattern>*.jsonp</url-pattern>
</filter-mapping>
bean mapping in applicationContext.xml
<bean id="jsonpCallbackFilter" class="com.world2.utils.spring.filters.jsonp.JsonpCallbackFilter" />
Controller:
#Controller
public class ApiRequests {
protected final Logger logger = Logger.getLogger(ApiRequests.class);
#RequestMapping(value = "api.json", produces = {"application/json"}, method = RequestMethod.GET)
#ResponseBody
public String handleActionJson(final HttpServletRequest request){
return "{test: 'blaat'}";
}
#RequestMapping(value = "api.jsonp", produces = {"text/javascript", "application/javascript", "application/json"}, method = RequestMethod.GET)
#ResponseBody
public String handleActionJsonp(final HttpServletRequest request){
return "{test: 'blaat'}";
}
}
Url I try to call:
http://host:8084/MyProject/server/api.jsonp?callback=test&_=1364980436087
Debug output:
11:13:57,776 DEBUG JsonpCallbackFilter:57 - test({test: 'blaat'});
11:13:57,776 DEBUG JsonpCallbackFilter:58 - Length: 22
Result:
test({test: 'bl
instead of expected result:
test({test: 'blaat'});
Apparently I had to first set the content length and then write the ouput.
Right:
wrapper.setContentType("text/javascript;charset=UTF-8");
wrapper.setContentLength(jsonpResponse.length);
out.write(jsonpResponse);
Wrong:
out.write(jsonpResponse);
wrapper.setContentType("text/javascript;charset=UTF-8");
wrapper.setContentLength(jsonpResponse.length);