1

We are building a webapp that communicates with a remote API. I would like to design the client for this remote API like this:

def RemoteApi
  constructor (username, password)
  getCurrentUser()  //implementation will use username and password
  getEmployees()  //implementation will use username and password
  ...

The point being, I want to pass in the credentials to this client during construction, and have all the other methods use these credentials. My second requirement is I want this RemoteApi instance to be in the session.

I have found out how to pass dynamic constructor arguments here.

I have found out how to create a session attribute here.

But I can't figure out a way to combine these two techniques. From what I gather, you have to instantiate a session attribute in its own getter-like method. This getter-like method won't have access to the form's fields so I won't be able to pass in credentials at this point.

Here's where I'm stuck:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;

import java.util.Map;

@Controller
@SessionAttributes("remoteClient")
public class LoginController {

    @Value("${company.name}")
    private String companyName;

    @Autowired
    private RemoteClient remoteClient;

    @ModelAttribute("remoteClient")
    public RemoteClient addRemoteClientToSession() {
        return remoteClient;  //how do initialize this with credentials in here?
    }

    @RequestMapping("/")
    public String showLogin(Model model) {
        model.addAttribute("credentials", new Credentials());
        return "login";
    }

    @RequestMapping("/login")
    public String login(@ModelAttribute("credentials") Credentials credentials, Map<String, Object> model) {
        if (remoteClient.auth(companyName, credentials.getUsername(), credentials.getPassword())) {
            model.put("fullname", remoteClient.findCurrentUser().getName());
            return "projectView";
        } else {
            return "login";
        }
    }
}

Update: Maybe the solution has to do with this technique. I didn't know about ModelAndView before this.

Community
  • 1
  • 1
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356

1 Answers1

0

The article I linked to in the question did have the necessary technique in it. What I would do is create an @Autowired RemoteApiFactory that has a newInstance(Credentials) method and pass that into addObject. The implementation of newInstance would probably end up using the new keyword. If anyone knows how to avoid this detail, I'd love to hear it.

Here's its example tweaked for my needs:

@Controller
@SessionAttributes("remoteApi")
public class SingleFieldController {

    @Autowired
    private RemoteApiFactory remoteApiFactory;    

    @RequestMapping(value="/single-field")
    public ModelAndView singleFieldPage() {
        return new ModelAndView("single-field-page");
    }

    @RequestMapping(value="/remember")  
    public ModelAndView rememberThought(@MethodAttribute("credentials") Credentials credentials) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("remoteApi", remoteApiFactory.newInstance(credentials));
        modelAndView.setViewName("single-field-page");
        return modelAndView;
    }
}
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356