I have some spring #RestControllers methods that I would like to inject with a value that comes with every request as a request attribute(containing the user) something like:
#RestController
#RequestMapping("/api/jobs")
public class JobsController {
// Option 1 get user from request attribute as prop somehow
private String userId = "user1";
// Option 2 inject into method using aspect or something else
#RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Jobs>> getJobs() throws ResourceNotFoundException {
// currentUser is injected
this.getJobs(currentUser);
}
I know I can do that:
#RestController
#RequestMapping("/api/jobs")
public class JobsController {
#RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Jobs>> getJobs(HttpServletRequest request) throws ResourceNotFoundException {
String currentUser = null;
if (request.getAttribute("subject") != null) {
currentUser = request.getAttribute("subject").toString();
}
this.getJobs(currentUser);
}
But that would require me to add this code at every method in my program, which seems to me, to be a really bad practice.
Is there a way to achieve what I want?
If the answer do require aspect, a code example will be much appreciated since I only read about it, but never actually did something with aspect.
Update
The code i suggested can be simplified using this:
#RestController
#RequestMapping("/api/jobs")
public class JobsController {
#RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Jobs>> getJobs(#Value("#{request.getAttribute('subject')}" String currentUser) throws ResourceNotFoundException {
this.getJobs(currentUser);
}
But still require me to add that parameter at every method.
Can this parameter be injected to every method somehow?
You could use a Filter to populate a ThreadLocal<String> variable that stores that attribute:
public class MyFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
ContextHolder.setSubject(request.getAttribute('subject'));
chain.doFilter(request, response);
}
#Override
public void destroy() {
ContextHolder.removeSubject();
}
}
public class ContextHolder {
private static final ThreadLocal<String> SUBJECT = new ThreadLocal<String>() {
#Override
protected String initialValue() {
return "empty";
}
};
public static void setSubject(String subject) {
SUBJECT.set(subject);
}
public static String getSubject() {
return SUBJECT.get();
}
public static void removeSubject() {
SUBJECT.remove();
}
}
The filter will be configured to intercept all requests and populate the SUBJECT variable. By using a ThreadLocal, you make sure that each thread has it's own subject value. You can now get that value anywhere in your application by calling ContextHolder.getSubject():
#RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Jobs>> getJobs(HttpServletRequest request) throws ResourceNotFoundException {
this.getJobs(ContextHolder.getSubject());
}
You will also have to register the Filter in the web.xml file:
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
In case you had multiple attributes, you could use a ThreadLocal<Map<String, String>> variable instead.
Simply just add #ResuestAttribute in your rest contorller
#RestController
#RequestMapping(path="/yourpath")
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity getAll(
#RequestAttribute(value = "yourAttribute") Object
yourAttribute......
If you really want to know about attributes then you should check out spring's #RequestParam annotation. You'd use it like this:
#RequestMapping(value = "", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Jobs>> getJobs(#RequestParam("subject") String currentUser) throws ResourceNotFoundException {
this.getJobs(currentUser);
}
Related
i am studying spring 5 and i can not use #RequestMapping annotation and don't know why
#RequestMapping includes #Component annotation so I just thought I can use that
initRequest includes URL parameter by string
i just expected initRequest(/hello) parameter binds URL
here is my code
public class SimpleControllerTest extends AbstractDispatcherServletTest {
#Test
public void helloSimpleController() throws ServletException, IOException {
setClasses(HelloController.class);
initRequest("/hello").addParameter("name", "spring");
runService();
assertModel("message", "Hello spring");
assertViewName("/WEB-INF/view/hello.jsp");
}
#Test(expected=Exception.class)
public void noParameterHelloSimpleController() throws ServletException, IOException {
setClasses(HelloController.class);
initRequest("/hello");
runService();
}
#Component("/hello")
//#RequestMapping("/hello")
static class HelloController extends SimpleController {
public HelloController() {
this.setRequiredParams(new String[] {"name"});
this.setViewName("/WEB-INF/view/hello.jsp");
}
public void control(Map<String, String> params, Map<String, Object> model) throws Exception {
model.put("message", "Hello " + params.get("name"));
}
}
static abstract class SimpleController implements Controller {
private String[] requiredParams;
private String viewName;
public void setRequiredParams(String[] requiredParams) {
this.requiredParams = requiredParams;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
final public ModelAndView handleRequest(HttpServletRequest req,
HttpServletResponse res) throws Exception {
...
}
public abstract void control(Map<String, String> params, Map<String, Object> model) throws Exception;
}
}
You need to work on your Spring basics. Your understanding of which annotations do what is incorrect and incomplete. The following links provide good knowledge on these. Go through these, revise your code, and you will solve this problem without needing help.
Spring Framework Annotations
Spring Annotations - JournalDev
A class containing
package com.data.job.controller;
#Controller
public class SomeController implements SomeUtility {
#RequestMapping(value = { "/v3/jobs/upload" }, method = RequestMethod.POST)
#ResponseBody
public ServerResponse upload(UploadItem uploadItem, BindingResult result, HttpServletResponse servletResponse) throws IOException {
// SomeHandler method used in between
return response;
}
An interface with default method:
package com.data.upload.util;
public interface SomeUtility {
default String SomeHandler(String value) {
try {
if (null != value) {
long keyName = Double.valueOf((value.trim())).longValue();
return String.valueOf(keyName);
}
} catch (NumberFormatException nfe) {
}
return value;
}
}
but while calling controller its giving 404 handler not found. If I removed implementation of SomeUtility its working fine.
I am trying to add junit test case for my Spring Boot OncePerRequestFilter shouldNotFilter method logic. The logic works fine with real-time REST calls but junit case is failing. Any idea?.
Here is test code.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringFilterTest {
#Test
public void getHealthTest() throws Exception {
standaloneSetup(new PersonController()).addFilter(new SkipFilter()).build().perform(get("/health")).andExpect(status().isOk());
}
#Test
public void getPersonTest() throws Exception {
standaloneSetup(new PersonController()).addFilter(new SkipFilter()).build().perform(get("/person")).andExpect(status().isAccepted());
}
private class SkipFilter extends OncePerRequestFilter {
private Set<String> skipUrls = new HashSet<>(Arrays.asList("/health"));
private AntPathMatcher pathMatcher = new AntPathMatcher();
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
response.setStatus(HttpStatus.ACCEPTED.value());
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return skipUrls.stream().anyMatch(p -> pathMatcher.match(p, request.getServletPath()));
}
}
#RestController
#RequestMapping(value = "/")
private static class PersonController {
#GetMapping("person")
public void getPerson() {
}
#GetMapping("health")
public void getHealth() {
}
}
}
I am expecting both of junit #Test cases to be successful but health one is always failing(its using Filter).
Incase, if you want to replicate below is complete repo code.
https://github.com/imran9m/spring-filter-test
Below Expression evaluates to false with request.getServletPath() when /health
skipUrls.stream().anyMatch(p -> pathMatcher.match(p, request.getServletPath()));
Change to request.getRequestURI() to get the uri and below condition matches the path
skipUrls.stream().anyMatch(p -> pathMatcher.match(p, request.getRequestURI()));
I have a Java Spring controller.
I want to escape all quotes in my request (sanitize it for using it in SQL queries for example).
Is there a way to do that with Spring ?
Example :
#RequestMapping(method = RequestMethod.POST)
public List<String[]> myEndpoint(#RequestBody Map<String, String> params, #AuthenticationPrincipal Account connectedUser) throws Exception{
return myService.runQuery(params, connectedUser);
}
If you want to validate all your request parameters in controllers, you can use custom validators. For Complete info, check Complete Example
Brief Overview:
Validator Implementation
#Component
public class YourValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(YourPojoType.class);
}
#Override
public void validate(Object target, Errors errors) {
if (target instanceof YourPojoType) {
YourPojoType req = (YourPojoType) target;
Map<String, String> params = req.getParams();
//Do your validations.
//if any validation failed,
errors.rejectValue("yourFieldName", "YourCustomErrorCode", "YourCustomErrorMessage");
}
}
}
Controller
#RestController
public class YourController{
#Autowired
private YourValidator validator;
#RequestMapping(method = RequestMethod.POST)
public List<String[]> myEndpoint(#Valid YourPojoType req, BindingResult result, #AuthenticationPrincipal Account connectedUser) throws Exception{
if (result.hasErrors()) {
//throw exception
}
return myService.runQuery(params, connectedUser);
}
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
}
In Spring MVC, I can do this to get a value of items on the query string:
public void sendMessage(HttpServletResponse response,
#RequestParam("Session Id") String sessionId,
But how to I get the complete querystring as one long string? I.e. I don't want individual parameters from it, I want the whole thing?
Many thanks!
Add the HttpServletRequest as argument to the method, and get the query string from the request:
public void sendMessage(HttpServletRequest request,
HttpServletResponse response {
String queryString = request.getQueryString();
}
If you don't want to use HttpServletRequest in your controller, you can create HandlerMethodArgumentResolver that resolves query string.
Example:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface QueryString {
}
public class QueryStringResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
Annotation[] parameterAnnotations = parameter.getParameterAnnotations();
for (Annotation parameterAnnotation : parameterAnnotations) {
if (QueryString.class.isInstance(parameterAnnotation)) {
return true;
}
}
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
return request.getQueryString();
}
}
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="mypackage.QueryStringResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
public class MyController {
#RequestMapping(...)
public String someMethod(#QueryString String queryString) {
...
}
}
Something like this you need to do:
public void sendMessage(HttpServletResponse response,
#RequestParam("Session Id") String sessionId, HttpServletRequest request,..
{
String qString= request.getQueryString();
The Controller itself knows the contents of the entire query string.
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception
You can then get the full query string from:
request.getQueryString();