当前位置: 首页 > 知识库问答 >
问题:

JAX-RS2&Jersey(2.10):并不总是注入我的SessionScoped Bean

解晟
2023-03-14

我有一个@sessionscoped bean,一个带有@stateless注释和@inject the bean和@ejb我的EJB的Restful Jersey服务。

当我试图从浏览器的URL调用rest web服务时,服务会注入@inject UserBean。但是当我试图从@SessionScoped UserBean中的代码调用web服务时,它不会被注入。

豆子:

@SessionScoped
@Named("userBean")
public class UserBean implements Serializable {

@EJB
private UserEJB userEJB;

private User user = new User();

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

private String newStatusContent;

public String getNewStatusContent() {
    return newStatusContent;
}

public void setNewStatusContent(String content) {
    this.newStatusContent = content;
}

public void publishStatus() {
    if (newStatusContent != null && newStatusContent.trim().isEmpty() == false) {
        //Maybe a way to set manualy the user property of this bean here?
        Client client = ClientBuilder.newClient();
        Response response = client.target("http://localhost:8080/services/posts/status")
                .queryParam("content", newStatusContent)
                .request(MediaType.APPLICATION_JSON).get();
        newStatusContent = "";
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request
                = (HttpServletRequest) context.getExternalContext().getRequest();
        if (request.getRequestURI().equals("/index/profile/profile.xhtml") &&     request.getParameter("prettyname") != null && request.getParameter("prettyname").equals(user.getPrettyname())) {
        }
        if (response.getStatus() == 200) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Δημοσίευση", "Η νέα κατάσταση δημοσιεύτηκε επιτυχώς."));

        } else {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Δημοσίευση", "Συγγνώμη, κάτι πήγε στραβά με την νέα κατάσταση :("));

        }

    }

}

 public String login() throws ServletException {
    User authenticatedUser;
    if (userEJB.mailExists(user)) {
        if ((authenticatedUser = userEJB.authenticate(user)) != null) {
            user = authenticatedUser;
            //and store the api key for rest api
            // userEJB.storeApiKey(user.getUserId());
        } else {
            FacesContext.getCurrentInstance().addMessage("mail", new FacesMessage("Wrong password for this e-mail"));
            return "";
        }

    } else {
        user = userEJB.create(user);
    }

    if (getPrevURI().isEmpty() == false) {
        return getPrevURI();
    } else {
        return "/index/index.xhtml?faces-redirect=true";
    }

}
} 
@ApplicationPath("/services/*")
public class RestApplication extends javax.ws.rs.core.Application{

@Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(Arrays.asList(
        UserService.class, 
        MessageService.class, PostService.class));
}

}
@Path("/posts")
@Stateless
public class PostService {

@EJB
private PostEJB postEJB;

@Inject
UserBean userBean;

@Path("/status")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response publishStatus(@QueryParam("content") String content) {
    if (content.isEmpty() || userBean.getUser().getUserId() == null) {
        System.out.println("null user id or content but?" + content);
        return Response.status(Response.Status.BAD_REQUEST).build();
    }
    Post postStatus = new Post();
    postStatus.setContent(content.trim());
    postStatus.setPublisher(userBean.getUser());
    postStatus.setPublisherUserId(userBean.getUser().getUserId());
    postEJB.create(postStatus);
    System.out.println("post id : "+postStatus.getPostId());
    PostStatus status = new PostStatus(postStatus.getPostId());
    status.setReceiver(userBean.getUser());
    status.setReceiverId(userBean.getUser().getUserId());
    postEJB.postStatus(status);
    return Response.status(Response.Status.OK).build();
}

//*this is not the problem I tried to remove and still doesn't worked*
@PreDestroy
public void destruct() {
    postEJB.destruct();

  }
}
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee  http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
    bean-discovery-mode="all"
    >

 </beans>
  • 1: Login from my login.xhtml page,
  • 2: When I try:
    http://localhost:8080/services/posts/status?content=Test status

    我使用的是Glassfish 4.1、JDK 8.20、Jersey 2.10(模块Glassfish)。
    我做错了什么?为什么要处理浏览器的url,而不是从代码调用客户端?
    ----使用另一种方式----使用保存在加密Cookie(用于浏览器的/JS调用)和加密会话中的“API密钥”

    另一种方式来进行安全身份验证,但这并不是我想要的:

    ApiAuthenticator服务筛选器:

    public class ApiAuthenticator implements ClientRequestFilter {
    
    private static final StandardPBEStringEncryptor encryptor;
    
    static {
        encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword("jasypt");
    }
    
    public static String encrypt(String theString) {
        return encryptor.encrypt(theString);
    }
    
    public static String decrypt(String encryptedString) {
        return encryptor.decrypt(encryptedString);
    }
    private final String apikey;
    
    public ApiAuthenticator(String apikey) {
        this.apikey = apikey;
    }
    
    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        //requestContext.getCookies().putIfAbsent("apikey", new Cookie("apikey", apikey));
        requestContext.getHeaders().add("apikey", encrypt(apikey));
    
    }
    
    }
    

    PostService内编辑的publishStatus方法:

    @Path("/status")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response publishStatus(@QueryParam("content") String content, @Context HttpHeaders headers)   {
    
        String apikey = headers.getHeaderString("apikey");
        if (apikey == null) {
            apikey = headers.getCookies().get("apikey").getValue();
        }
    
        if (content.isEmpty() || apikey == null) {
            return Response.status(Response.Status.BAD_REQUEST).build();
        }
        User user = userEJB.findByApiKey(ApiAuthenticator.decrypt(apikey));
        Post postStatus = new Post();
        postStatus.setContent(content.trim());
        postStatus.setPublisher(user);
        postStatus.setPublisherUserId(user.getUserId());
        postEJB.create(postStatus);
        PostStatus status = new PostStatus(postStatus.getPostId());
        status.setReceiver(user);
        status.setReceiverId(user.getUserId());
        postEJB.postStatus(status);
        return Response.status(Response.Status.OK).build();
    }
    
     Client client = ClientBuilder.newClient();
    
            Response response = client.target("http://localhost:8080/services/posts/status")
                    .queryParam("content", newStatusContent)
                    .register(new ApiAuthenticator(apiEJB.getApiKey(user.getUserId()).getApiKey()))
                    .request(MediaType.APPLICATION_JSON).get();
    

    用户成功登录后在UserBean上编辑的登录方法:

       ((HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse())
                        .addCookie(new Cookie("apikey",   ApiAuthenticator.encrypt(apiEJB.storeApiKey(user.getUserId()).getApiKey())));
    

    RestApplication.java:

    @ApplicationPath("/services/*")
    public class RestApplication extends javax.ws.rs.core.Application{
    
    @Override
    public Set<Class<?>> getClasses() {
    return new HashSet<Class<?>>(Arrays.asList(
            ApiAuthenticator.class ,
            UserService.class, 
            MessageService.class, PostService.class));
    
    }
    
    
    }
    

    解决方案是@将PostService注入UserBean

    @Inject PostService postService;
    
    postService.publishStatus(newStatusContent);
    
    @ServerEndpoint(value = "/websocket", encoders = {MessageEncoder.class},
        decoders = {MessageDecoder.class}, configurator = GetHttpSessionConfigurator.class)
    
    public class ChatServerEndPoint {
    
    @PersistenceContext(unitName = "CosmosDBPeristenceUnit")
    private EntityManager em;
    
    private static Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());
    
    @Inject
    private MessageService messageService;
    //private Session session;
    private int thisUserId;
    private User thisUser;
    
    @OnOpen
    public void onOpen(Session thisSession, EndpointConfig config) throws IOException, EncodeException {
        thisUserId = (int) config.getUserProperties().get("userId");
        thisUser = em.find(User.class, thisUserId);
        thisSession.getUserProperties().put("userId", thisUserId);
    
        Iterator<Session> it = peers.iterator();
        while (it.hasNext()) {
            Session session = it.next();
            int userId = (int) session.getUserProperties().get("userId");
    
            thisSession.getBasicRemote().sendObject(new UserStatusMessage(em.find(User.class, userId), true));
            session.getBasicRemote().sendObject(new UserStatusMessage(thisUser, true));
        }
    
        peers.add(thisSession);
    }
    
    /* we don't want @stateless problems with chat info except message, I will do it with rest public   */
    @OnMessage
    public void onMessage(WebsocketMessage message) {
        try {
            Iterator<Session> it = peers.iterator();
            if (message instanceof ChatMessage) {
                ChatMessage msg = (ChatMessage) message;
                msg.setSender(thisUser);
                boolean otherUserIsOnline = false;
                while (it.hasNext()) {
                    Session receiver = it.next();
                    if ((int) receiver.getUserProperties().get("userId") == msg.getReceiver_userId()) {
                        receiver.getBasicRemote().sendObject(msg);
                        otherUserIsOnline = true;
                        break;
                    }
                }
                if (otherUserIsOnline == false) {
                    //save the message to database via rest
                    messageService.addMessage(msg.getReceiver_userId(), msg.getMessage());
                }
            } else {
                while (it.hasNext()) {
                    Session receiver = it.next();
                    receiver.getBasicRemote().sendObject(message);
                }
            }
    
        } catch (IOException | EncodeException ex) {
            System.out.println("Exception on endpoint: " + ex.getMessage());
        }
    }
    
    @OnClose
    public void onClose(Session session) throws IOException, EncodeException {
        peers.remove(session);
        Iterator<Session> it = peers.iterator();
        while (it.hasNext()) {
            Session otherSession = it.next();    
            otherSession.getBasicRemote().sendObject(new UserStatusMessage(thisUser, false));
    
        }
    
    }
    
    @OnError
    public void onError(Throwable t) {
        t.printStackTrace();
    }
    
    }
    
    public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    
    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        HttpSession ses = (HttpSession) request.getHttpSession();
        config.getUserProperties().put("userId", ses.getAttribute("userId"));
    }
    }
    

    java:

    @Stateless
    @Path("/messages")
    public class MessageService {
    
    @EJB
    UserEJB userEJB;
    
    @EJB
    ConversationEJB convEJB;
    
    @Inject
    UserBean userBean;
    
    
    @Path("/{otherUserId}")
    @GET
    @Produces("application/json; charset=UTF-8")
    public List<Message> findConversation(@PathParam("otherUserId") int otherUserId) {
        User otherUser = userEJB.findById(otherUserId);
    
        return convEJB.findConversation(userBean.getUser(), otherUser);
    
    }
    
    
    @Path("/{receiverUserId}")
    @POST
    @Produces("application/json; charset=UTF-8")
    public Message addMessage(@PathParam("receiverUserId") int receiverUserId, @QueryParam("body") String body) {
        return convEJB.addMessage(userBean.getUser(), userEJB.findById(receiverUserId), body);
    }
    
    @Path("/{messageId}")
    @DELETE
    public void removeMessage(@PathParam("messageId") int messageId) {
        convEJB.removeMessage(messageId);
    }
    
    @Path("/{userId}/{otherUserId}")
    @DELETE
    public void clearConversation(@PathParam("userId") int userId, @PathParam("otherUserId") int otherUserId) {
        convEJB.clearConversation(userEJB.findById(userId), userEJB.findById(otherUserId));
    }
    
    }
    

    提前感谢!


共有1个答案

狄珂
2023-03-14

由于我不知道应用程序的确切流程,这可能是错误的,但这里是我的假设:
您的userBean是会话范围,它保存用户数据。不知何故,在你的应用程序中,它可能是初始化的。

因此,一旦您从浏览器调用服务,它就会发送会话cookie来正确定位会话数据。

但是当您从代码中调用服务时,它不会传递那个会话cookie,所以在服务器端它会创建全新的会话,从而创建具有空用户数据的新userBean。这就是为什么用户ID为空。Bean本身被注入,否则这里将有空指针:

userBean.getUser().getUserId() // as userBean would be null
 类似资料:
  • 这是我的代码,我不知道为什么我的bean没有被注入,它在构造函数中总是空的 下面是我的配置类: 我的堆栈跟踪 启动ApplicationContext时出错。若要显示自动配置报告,请在启用“debug”的情况下重新运行应用程序。11:55:29.139[restartedMain]错误O.S.Boot.SpringApplication-应用程序启动失败org.SpringFramework.Be

  • 例如,如果几个资源endpoint需要访问某个消息总线来处理请求,该怎么办?当然,当服务类本身不是资源但被资源使用时,有一些方法可以注册一个单例服务类并将其注入到资源中。 我看到的提供程序或自定义HK2绑定的所有示例都引用了资源。 我发现的最接近我所要找的是这个问题: 使用内置的Jersey依赖注入在Jersey 2中创建一个简单的单例类会遇到麻烦 请注意,编程方式将是最有用的,我没有使用xml文

  • 所以我有一个类需要测试。我们把它叫做ClassToTest。它有两个Dao对象作为字段。 正如您所看到的,ClassToTest不包含任何构造函数或setter,我正在使用spring自动关联字段。 现在,我有了一个具有classToTest所需的所有依赖项的基本测试类: 并且testClass扩展了这个BaseTest类: 这将导致保存时出现空指针异常。但是,如果我将设置方法更改为: 考试通过了

  • 将EntityManager注入资源可以工作,但不能注入Callable。在这里,EntityManager保持。 请告知代码保存在这里是否比保存在GitHub上更好。

  • 我们可以通过 实现创建JAX-RS示例。 为此,需要加载 jersey相关jar文件或使用Maven框架。 在这个例子中,我们使用jersey jar文件来实现JAX-RS jersey示例。 Jersey Jar文件下载网址:https://jersey.github.io/download.html 打开Eclipse,创建一个Web工程: restfuljersey,如下图所示 - JAX-

  • 我正在学习Jersey/JAX-RS,需要一些ExceptionMapper方面的帮助。 我有一个UserFacade类、AbstractFacade类和User类本身,它们都是非常标准的,主要是通过在NetBeans中创建一个新的Web服务RestFUL项目生成的。我的问题是,我想现在开始捕捉错误,比如“唯一约束违反”错误。我想我需要实现一个异常映射器...我的门面有以下内容: 这是我得到的错误