Java – reads the httprequest content from the spring exception handler

I use spring's @ exceptionhandler annotation to catch exceptions in my controller

Some requests save post data as a pure XML string written to the request body, and I want to read the data to record exceptions The problem is that when I request the input stream in the exception handler and try to read it, the stream returns - 1 (null)

The signature of the exception handler is:

@ExceptionHandler(Throwable.class)
public ModelAndView exception(HttpServletRequest request,HttpServletResponse response,HttpSession session,Throwable arff)

Any ideas? Is there any way to access the request body?

My controller:

@Controller
@RequestMapping("/user/**")
public class UserController {

    static final Logger LOG = LoggerFactory.getLogger(UserController.class);

    @Autowired
    IUserService userService;


    @RequestMapping("/user")
    public ModelAndView getCurrent() {
        return new ModelAndView("user","response",userService.getCurrent());
    }

    @RequestMapping("/user/firstLogin")
    public ModelAndView firstLogin(HttpSession session) {
        userService.logUser(session.getId());
        userService.setOriginalAuthority();
        return new ModelAndView("user",userService.getCurrent());
    }


    @RequestMapping("/user/login/failure")
    public ModelAndView loginFailed() {
        LOG.debug("loginFailed()");
        Status status = new Status(-1,"Bad login");
        return new ModelAndView("/user/login/failure",status);
    }

    @RequestMapping("/user/login/unauthorized")
    public ModelAndView unauthorized() {
        LOG.debug("unauthorized()");
        Status status = new Status(-1,"Unauthorized.Please login first.");
        return new ModelAndView("/user/login/unauthorized",status);
    }

    @RequestMapping("/user/logout/success")
    public ModelAndView logoutSuccess() {
        LOG.debug("logout()");
        Status status = new Status(0,"Successful logout");
        return new ModelAndView("/user/logout/success",status);

    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.POST)
    public ModelAndView create(@RequestBody UserDTO userDTO,@PathVariable("id") Long id) {
        return new ModelAndView("user",userService.create(userDTO,id));
    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public ModelAndView getUserById(@PathVariable("id") Long id) {
        return new ModelAndView("user",userService.getUserById(id));
    }

    @RequestMapping(value = "/user/update/{id}",method = RequestMethod.POST)
    public ModelAndView update(@RequestBody UserDTO userDTO,userService.update(userDTO,id));
    }

    @RequestMapping(value = "/user/all",method = RequestMethod.GET)
    public ModelAndView list() {
        return new ModelAndView("user",userService.list());
    }

    @RequestMapping(value = "/user/allowedAccounts",method = RequestMethod.GET)
    public ModelAndView getAllowedAccounts() {
        return new ModelAndView("user",userService.getAllowedAccounts());
    }

    @RequestMapping(value = "/user/changeAccount/{accountId}",method = RequestMethod.GET)
    public ModelAndView changeAccount(@PathVariable("accountId") Long accountId) {
        Status st = userService.changeAccount(accountId);
        if (st.code != -1) {
            return getCurrent();
        }
        else {
            return new ModelAndView("user",st);
        }
    }
    /*
    @RequestMapping(value = "/user/logout",method = RequestMethod.GET)
    public void perlogout(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
        userService.setOriginalAuthority();
        response.sendRedirect("/marketplace/user/logout/spring");
    }
     */

    @ExceptionHandler(Throwable.class)
public ModelAndView exception(HttpServletRequest request,Throwable arff) {
    Status st = new Status();
    try {
        Writer writer = new StringWriter();
        byte[] buffer = new byte[1024];

        //Reader reader2 = new BufferedReader(new InputStreamReader(request.getInputStream()));
        InputStream reader = request.getInputStream();
        int n;
        while ((n = reader.read(buffer)) != -1) {
            writer.toString();

        }
        String retval = writer.toString();
        retval = "";
        } catch (IOException e) {

            e.printStackTrace();
        }

        return new ModelAndView("profile",st);
    }
}

thank you

Solution

When I read from InputStream, I have tried your code and found some errors in the exception handler:

Writer writer = new StringWriter();
byte[] buffer = new byte[1024];

//Reader reader2 = new BufferedReader(new InputStreamReader(request.getInputStream()));
InputStream reader = request.getInputStream();
int n;
while ((n = reader.read(buffer)) != -1) {
    writer.toString();

}
String retval = writer.toString();
retval = "";

I replaced your code with this:

BufferedReader reader = new BufferedReader(new   InputStreamReader(request.getInputStream()));
String line = "";
StringBuilder stringBuilder = new StringBuilder();
while ( (line=reader.readLine()) != null ) {
    stringBuilder.append(line).append("\n");
}

String retval = stringBuilder.toString();

Then I can read it from the InputStream in the exception handler, and it works! If you still cannot read from InputStream, it is recommended that you check how to send XML data to the request body You should consider using InputStream only once per request, so it is recommended that you check if there are no other calls to getinputstream() If you have to call it two or more times, you should write such a custom httpservletrequestwrapper to create a copy of the request body so that you can read it again

Update your comments helped me reproduce this problem You use the @ requestbody annotation, so you don't call getinputstream (), but spring will call it to retrieve the body of the request Look at class org springframework. web. bind. annotation. support. Handlermethodinvoker: if you use the @ requestbody class to call the resolverequestbody method, etc... finally, you can no longer read InputStream from your ServletRequest If you still want to use @ requestbody and getinputstream () in your own methods, you must package the request into a custom httpservletrequestwrapper to create a copy of the request body so that you can manually read it multiple times This is my packing

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private static final Logger logger = Logger.getLogger(CustomHttpServletRequestWrapper.class);
    private final String body;

    public CustomHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);

        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;

        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line = "";
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line).append("\n");
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
            logger.error("Error reading the request body...");
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    logger.error("Error closing bufferedReader...");
                }
            }
        }

        body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final StringReader reader = new StringReader(body);
        ServletInputStream inputStream = new ServletInputStream() {
            public int read() throws IOException {
                return reader.read();
            }
        };
        return inputStream;
    }
}

Then you should write a simple filter to wrap the request:

public class MyFilter implements Filter {

    public void init(FilterConfig fc) throws ServletException {

    }

    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException {
        chain.doFilter(new CustomHttpServletRequestWrapper((HttpServletRequest)request),response);

    }

    public void destroy() {

    }

}

Finally, you must be on the web Configure filters in XML:

<filter>     
    <filter-name>MyFilter</filter-name>   
    <filter-class>test.MyFilter</filter-class>  
</filter> 
<filter-mapping>   
    <filter-name>MyFilter</filter-name>   
    <url-pattern>/*</url-pattern>   
</filter-mapping>

You can only start the filter for the controller you really need, so you should change the URL format as needed

If you only need this function in one controller, you can also receive a copy of the request body in the controller through the @ requestbody annotation

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>