如何将Java对象序列化为JSON并在servlet过滤器中返回?

4
我有一个javax.servlet.Filter,用于检查客户端是否被允许访问API REST资源。
@Component
public class AuthorizationRequestFilter implements Filter {

    public static final String AUTHORIZATION_TOKEN = "X-Access-Token";

    @Autowired
    @Qualifier("loginService")
    private ILoginService loginService;

    private void throwUnauthorized(ServletResponse res) throws IOException {

        HttpServletResponse response = (HttpServletResponse) res;

        response.reset();
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);

    }

    private void throwForbidden(ServletResponse res) throws IOException {

        HttpServletResponse response = (HttpServletResponse) res;

        response.reset();
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.sendError(HttpServletResponse.SC_FORBIDDEN);

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        String accessToken = request.getHeader(AUTHORIZATION_TOKEN);

        if (StringUtils.isEmpty(accessToken)) {
            throwUnauthorized(res);
        } else {
            AccountLoginData account = loginService.find(accessToken);
            if (account == null) {
                throwForbidden(res);
            }
        }

        chain.doFilter(req, res);

    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }

}

这段代码可以正常运行,但我想在这两个throw*()方法中向客户端返回适当的JSON信息。在应用程序的另一部分,我使用这些响应消息对象来告知客户端发生了什么。

例如,当记录未被找到时:

public class NotFoundResponseMessage extends ResponseMessage {

    public NotFoundResponseMessage(String message) {
        super(HttpStatus.NOT_FOUND, 1, message);
    }

}

并且

public class ResponseMessage {

    private int status;
    private int code;
    private String message;
    private String reason;

    public ResponseMessage(int status, int code, String message, String reason) {

        Assert.notNull(reason, "Reason must not be null.");
        Assert.isTrue(status > 0, "Status must not be empty.");

        this.status = status;
        this.code = code;
        this.message = message;
        this.reason = reason;

    }

}

我的问题

我想在我的javax.servlet.Filter授权/认证过滤器中,使用Spring Boot和Jackson库返回已序列化的对象(UnauthorizedResponseMessageForbiddenResponseMessage)的JSON。

  1. 如何手动ResponseMessage序列化为JSON表示形式?
  2. 如何在我的过滤器类中将此JSON写回客户端?

编辑1:

private void throwUnauthorized(ServletResponse res) throws IOException {

    HttpServletResponse response = (HttpServletResponse) res;

    response.reset();
    response.setHeader("Content-Type", "application/json;charset=UTF-8");
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.getWriter().write("{\"foo\":\"boo\"}");

}

现在我可以写出JSON,但是返回HTTP 500,因为:
java.lang.IllegalStateException: getWriter() has already been called for this response
    at org.apache.catalina.connector.Response.getOutputStream(Response.java:544)

1
据我所知,您只需要在已验证的情况下执行chain.doFilter(req, res);。否则,链中的其他事物会尝试添加常规(重复)响应:https://dev59.com/gWoy5IYBdhLWcg3wl_Ix#8445927 - zapl
3个回答

3
使用 JacksonObject 转换为 JSON,以下是一个示例。
ObjectMapper mapper = new ObjectMapper();
String Json =  mapper.writeValueAsString(object);  

为什么这个答案被踩了?它有什么问题吗(它是有效的)。 - Artegon

1

我有同样的问题,完整的解决方案如下:

try {
    restResponse = service.validate(httpReq);
} catch (ForbiddenException e) {

    ObjectMapper mapper = new ObjectMapper();
    ResponseObject object = new ResponseObject();
    object.setStatus(HttpServletResponse.SC_FORBIDDEN);
    object.setMessage(e.getMessage());
    object.setError("Forbidden");
    object.setTimestamp(String.valueOf(new Date().getTime()));
    HttpServletResponse httpResp = (HttpServletResponse) response;
    httpResp.reset();
    httpResp.setHeader("Content-Type","application/json;charset=UTF-8");
    httpResp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    String json =  mapper.writeValueAsString(object);
    response.getWriter().write(json);
    return;
}

结果是:

enter image description here


-2

只需从过滤器中抛出异常,并使用@ResponseStatus注释抛出的异常。这样,它会自动转换为给定的HTTP错误代码。(您还可以定义错误消息)

代码示例:

@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Error while trying to add the feed.")
public class AddFeedException extends Exception {

    private static final long serialVersionUID = 290724913968202592L;

    public AddFeedException(Throwable throwable) {
        super(throwable);
    }
}

2
是的,我在控制器中使用这个,但是在javax.servlet.Filter中的异常处理不起作用,因为它在Spring的servletDispatcher之外... - Artegon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接