I made web service with spring mvc(version 4).
This service used token in http header for authorization.
I want to value in http header bind to field in model class auto.
Is it possible? How can I do?
(See below code and comment)
Controller
#Controller
#RequestMapping(value = "/order")
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
#Autowired
private OrderService orderService;
#RequestMapping(value = "/")
#ResponseBody
public List<Order> getAll() throws Exception {
// I want to remove two line below with auto binding (userToken field in model)
// in all controller using token value
String token = request.getHeader("X-Auth-Token"); // remove~
orderService.setUserToken(token); // remove~
orderService.getAllbyUser()
return items;
}
}
Model(Service)
#Service
public class OrderService {
//#Autowired - is it possible?
private String userToken;
public String setUserToken(String userToken)
{
this.userToken = userToken;
}
public List<Order> getAllbyUser() {
String userId = userMapper.getUserId(userToken);
List<Order> list = orderMapper.getAllbyUser(userId);
return list;
}
}
#Autowire is for Spring to inject beans one to another. If you want to inject a String to a bean you can with the org.springframework.beans.factory.annotation.Value annotation.
For example:
#Value("${user.token}")
private String userToken;
This will make Spring search of the user.token in the VM args and other places (which I don't remember and in some specific order).
But again, as said in my initial comment, from the code you show here it seems to be an error setting this field as it is context specific and the #Service (by default) indicates that the OrderService is a singleton.
In order to read a header value from request, you can use #RequestHeader("X-Auth-Token") in your controller, as shown below:
#RequestMapping(value = "/")
#ResponseBody
public List<Order> getAll(#RequestHeader("X-Auth-Token") String token) throws Exception {
orderService.setUserToken(token); // remove~
orderService.getAllbyUser()
return items;
}
Hope this helps you.
Related
What should be the scope of Controller and RestController in a Spring application? The default behavior if being Singleton. But will not having single bean cross over the request/responses from multiple clients, as our Controller will call some other bean (say #Service) which handles user specific request (like fetching user details from a DB or from another REST/SOAP service).
You have two options here:
Option 1 - In your service classes, ensure that you do not save any request specific details in instance variables. Instead pass them around as method arguments.
Ex:
The below code will corrupt the value stored in userId if there are simultaneous requests.
#Service
public class SomeService {
String userId;
public void processRequest(String userId, String orderId) {
this.userId = userId;
// Some code
process(orderId);
}
public void process(String orderId) {
// code that uses orderId
}
}
While, the following code is safe.
#Service
public class SomeService {
private String userId;
public void processRequest(String userId, String orderId) {
// Some code
process(userId, orderId);
}
public void process(String userId, String orderId) {
// code that uses userId and orderId
}
}
Option 2:
You can save request specific data in request scoped beans and inject them in your singletons. Spring creates a proxy for the injected request scoped beans and proxies calls to the bean associated with the current request.
#RequestScoped
class UserInfo {
}
#Service
class UserService {
#Autowired
private UserInfo userInfo;
public void process(String orderId) {
// It is safe to invoke methods of userInfo here. The calls will be passed to the bean associated with the current request.
}
}
I have the following controller:
#RepositoryRestController
public class TestController {
#RequestMapping(value = "/testables", method = RequestMethod.GET)
public String get(){
return "testin it";
}
}
And it is not picked up by Spring. I get 404 when I hit /apiroot/testables address. If I change it to be #RestController and add the api root to request mapping value, then it works. Also, if I change the mapping to point to "/orders/testables", it works as #RepositoryRestController. I also have the following controller in the same package which works fine:
#RepositoryRestController
public class SendEmailController {
#Autowired
private MessageSource messageSource;
#Autowired
private JavaMailSender javaMailSender;
#Autowired
private OrderRepository orderRepository;
#RequestMapping(value = "/orders/{id}/sendOrderEmailToSupplier")
public void sendOrderEmailToSupplier(#PathVariable("id") Long id, WebRequest request) throws MessagingException {...
#RepositoryRestController deal with basePath only for resources which it manage. In other cases the path should not contain the "base".
If you want to build custom operations underneath basePath, you can use #BasePathAwareController.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ServerApplication.class)
public class ExampleTest {
public static final String EXAMPLE = "/example";
//this is the TestRestTemplate used
#Autowired
TestRestTemplate testRestTemplate;
//this is the test
#Test
public void testExample() {
String s = "asd";
Object response = testRestTemplate.postForEntity(EXAMPLE, s, String.class ,Collections.emptyMap());
}
}
This is the tested endpoint:
#RestController
public class ServerController {
#ResponseBody
#PostMapping("/example")
public String exampleEndpoint (String in) {
return in;
}
}
#SpringBootApplication
#ComponentScan("com.company.*")
public class ServerApplication {
etc.
When I debug it, at the exampleEndpoint the in parameter is always null.
I'm using Spring Boot 1.4 Spring 4.3.2
I changed the names and removed all the company stuff, but otherwise this is how it is, it works in the sense that it hits the endpoint, it just that the request doesn't go through.
You need to annotate the argument in your method exampleEndpoint with RequestBody like so:
public String exampleEndpoint (#RequestBody String in) {
return in;
}
i have created a custom rest controller and I can access the API and get the result from the resource, the problem is, it doesn't appear in the HAL Browser.. how to expose this custom method in the HAL Browser? Thank You...
#RepositoryRestController
public class RevisionController {
protected static final Logger LOG = LoggerFactory
.getLogger(RevisionController.class);
private final DisciplineRepository repository;
Function<Revision<Integer, Discipline>, Discipline> functionDiscipline = new Function<Revision<Integer, Discipline>, Discipline>() {
#Override
public Discipline apply(Revision<Integer, Discipline> input) {
return (Discipline) input.getEntity();
}
};
#Inject
public RevisionController(DisciplineRepository repository) {
this.repository = repository;
}
#RequestMapping(method = RequestMethod.GET, value = "/disciplines/search/{id}/revisions")
public #ResponseBody ResponseEntity<?> getRevisions(
#PathVariable("id") Integer id) {
Revisions<Integer, Discipline> revisions = repository.findRevisions(id);
List<Discipline> disciplines = Lists.transform(revisions.getContent(),
functionDiscipline);
Resources<Discipline> resources = new Resources<Discipline>(disciplines);
resources.add(linkTo(
methodOn(RevisionController.class).getRevisions(id))
.withSelfRel());
return ResponseEntity.ok(resources);
}
}
Register a bean that implements a ResourceProcessor<RepositoryLinksResource> and you can add links to your custom controller to the root resource, and the HAL Browser will see it.
public class RootResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource resource) {
resource.add(ControllerLinkBuilder.linkTo(ControllerLinkBuilder.methodOn(RevisionController.class).getRevisions(null)).withRel("revisions"));
return resource;
}
}
This are the annotated methods in the controller:
#RequestMapping(method = RequestMethod.GET)
public String getClient(#PathVariable("contractUuid") UUID contractUuid, Model model) {
ClientDto clientDto = new ClientDto();
clientDto.setContractUuid(contractUuid);
model.addAttribute("client", clientDto);
return "addClient";
}
#ModelAttribute("contract")
public ContractDto getContract(#PathVariable("contractUuid") UUID contractUuid) throws ContractNotFoundException {
return contractService.fromEntity(contractService.findByUuid(contractUuid));
}
The test method that I am trying is shown below, but it fails for attribute contract. The attribute client is added to the Model in a #RequestMapping method.
private MockMvc mockMvc;
#Autowired
private ContractService contractServiceMock;
#Autowired
private ClientService clientServiceMock;
#Autowired
protected WebApplicationContext wac;
#Before
public void setup() {
Mockito.reset(contractServiceMock);
Mockito.reset(clientServiceMock);
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void test() throws Exception {
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
mockMvc.perform(get("/addClient/{contractUuid}", uuid))
.andExpect(status().isOk())
.andExpect(view().name("addClient"))
.andExpect(forwardedUrl("/WEB-INF/pages/addClient.jsp"))
.andExpect(model().attributeExists("client"))
.andExpect(model().attributeExists("contract"));
}
The contract attribute shows in the jsp page when I run the application, since I use some of its attributes, but since it fails in the test method is there another way to test it ?
It fails with the message:
java.lang.AssertionError: Model attribute 'contract' does not exist
Spring is 4.0.1.RELEASE
It seems it was my fault.
Even though the #ModelAttribute method returns an instance of ContractDto I only mocked one method used from the service:
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
and so findByUuid returned something, but contractService.fromEntity was left untouched so I had to also mock it:
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
ContractDto contractDto = new ContractDto(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
when(contractServiceMock.fromEntity(contract)).thenReturn(contractDto);