Define a base rest controller in spring boot? - spring-mvc

Originally I just had MyRestController
#CrossOrigin(origins = "*")
#RestController
public class MyRestController {
#RequestMapping(value = "/v1/endpoint", method = {RequestMethod.GET})
public ResponseEntity<Object> endpoint(HttpServletRequest request,
HttpServletResponse response) {
// etc - duplicate code across controllers with the one
// difference of a single function call and its corresponding params
}
}
Then I realized that a lot of the functionality was reused across 6 other controllers so I consolidated them using an abstract BaseController
abstract class BaseController {
public ResponseEntity<Object> run(String path, String[] params) {
Object result = null;
switch (path.toLowerCase()) {
// case for each path
case MY_PATH:
result = someService.myPath(param[0]);
break;
case MY_OTHER_PATH:
result = someService.myOtherPath(param[0], param[1]);
break;
default:
System.out.println("No");
throw new Exception();
}
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
and then I changed the class header for MyRestController to
public class MyRestController extends BaseController {
and this worked!
My questions are:
How come I couldn't move the CrossOrigin from MyRestController to BaseController ?
I was told to use an abstract class. Does this help at all in this use case?
I replaced the duplicate try / catch with a single function using the path in a switch statement to use the correct method with the correct params. This seems hackish... Is there a better way to do this?
Thanks

Looking at the documentation for the annotation CrossOrigin https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html
This is why it didn't work is because your BaseController doesn't have any methods to add to the RequestMappingHandlerMapping so that is why it didn't work.
As far as the BaseController being abstract it is not necessary unless you have a method that you want your extending controllers to overwrite.
Update about Switch
That is really up to you depending on how many controller methods are going to fall into each case. If multiple methods fall into the MY_PATH case then I believe you are ok, but I can see where the cases could turn into a very lengthy switch statement. It's really up to you as the maintainer. In my opinion, I would break the switch statement cases into different methods and let the controller that extend call that method. But that is personal preference.

Related

How to target specific handlers with a #ControllerAdvice #ModelAttribute?

I'd like to display a warning message on specific pages 5 minutes prior to a system shutdown. Rather than add it manually to each these pages I created a #ControllerAdvice class with a #ModelAttribute method that adds the message to the Model parameter, but from what I understand reading the documentation and SO and some initial testing this model attribute will be added to every method with a #RequestMapping.
I realize I could refactor my code so that the targeted methods are all in one controller and limit the #ControllerAdvice to that one controller, but I would end up with a collection of otherwise non-related methods in that controller which muddies up the overall structure of my controllers.
So, is there a way to indicate which specific methods in multiple controllers the #ModelAttribute is applied to? Would a custom annotation be a solution (not sure how that would work)? I'd like to do this via annotations if possible.
Edit:
The #ControllerAdvice code is pretty basic:
#ControllerAdvice
public class GlobalModelController {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private MaintenanceInterceptor maintInterceptor;
#ModelAttribute()
public void globalAttributes(Model model, Locale locale) {
if (maintInterceptor.isMaintenanceWindowSet() && !maintInterceptor.isMaintenanceInEffect()) {
String msg = maintInterceptor.getImminentMaint(locale);
model.addAttribute("warningMaint", msg);
logger.debug("maint msg= " + msg);
}
}
}
A controller advice can be limited to certain controllers (not methods) by using one of the values of the #ControllerAdvice annotation, e.g.
#ControllerAdvice(assignableTypes = {MyController1.class, MyController2.class})
If you need to do it on a method level I suggest to take a look at Interceptors.
Thanks to #zeroflagL for pointing me to the interceptor solution. I ditched the #ControllerAdvice approach and ended up with this:
Custom annotation:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface MaintAware {
String name() default "MaintAware";
}
Interceptor:
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();
MaintAware maintAware = method.getAnnotation(MaintAware.class);
if (maintAware != null) {
Locale locale = request.getLocale();
if (isMaintenanceWindowSet() && !isMaintenanceInEffect()) {
String msg = getImminentMaint(locale);
if (!msg.isEmpty())
modelAndView.addObject("warningMaint", msg);
}
}
super.postHandle(request, response, handler, modelAndView);
}
Now I can annotate the specific methods that require the maintenance notification. Easy peasy. :)

Spring Security: separating controller by user role

With #RequestMapping, request can be associated with different controller functions through header or request parameters. Is there a way to achieve this base on the user user role? The aim is avoid if statement in the controller.
As far as I am aware, there is not anything that comes out of the box, but if you wanted to you could probably create a custom mapping annotation to do this routing for you.
I have not actually tried any of this code, but something like:
Your new annoation, used like #UserRoleMapping("ROLE_ADMIN")
#Target( ElementType.TYPE )
#Retention(RetentionPolicy.RUNTIME)
public #interface UserRoleMapping {
String[] value();
}
Next, you can just extend the standard Spring RequestMappingHandlerMapping class (this is the class that handles the standard mapping of #RequestMapping annotations). You just need to tell the mapping handler to also take into account a custom condition:
public class UserRoleRequestCondition extends RequestMappingHandlerMapping {
#Override protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
UserRoleMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, UserRoleMapping.class);
return (typeAnnotation != null) ? new UserRoleRequestCondition( typeAnnotation.value() ) : null;
}
}
The above code just checks the controller for your new annotation created above and if it is found it returns a new condition class, constructed with the value you have set in the annotation (e.g. "ROLE_ADMIN"). This MappingHandler will need to be set in your Spring config (whereever you are currently setting the RequestMappingHandlerMapping, just replace it with one of these).
Next we need to create the custom condition - this is the guy that is going to be invoked on request to determine if a request matches the controller:
public class UserRoleRequestCondition implements RequestCondition<UserRoleRequestCondition> {
private final Set<String> roles;
public UserRoleRequestCondition( String... roles ) {
this( Arrays.asList(roles) );
}
public UserRoleRequestCondition( Collection<String> roles ) {
this.roles = Collections.unmodifiableSet(new HashSet<String>(roles));
}
#Override public UserRoleRequestCondition combine(UserRoleRequestCondition other) {
Set<String> allRoles = new LinkedHashSet<String>(this.roles);
allRoles.addAll(other.roles);
return new UserRoleRequestCondition(allRoles);
}
#Override public UserRoleRequestCondition getMatchingCondition( HttpServletRequest request ) {
UserRoleRequestCondition condition = null;
for (String r : roles){
if ( request.isUserInRole( r ) ){
condition = this;
}
}
return condition;
}
#Override public int compareTo(UserRoleRequestCondition other, HttpServletRequest request) {
return (other.roles - this.roles).size();
}
}
In the above, the method getMatchingCondition is where we match the request. (apologies if I have missed some semi-colons or return keywords etc - this is based on groovy, but hopefully if you are in java you can work out where those bits go!)
Props to Marek for his more detailed answer on the more fully-formed solution to custom routing based on the subdomain that I used when I had to implement something similar! How to implement #RequestMapping custom properties - That gives more details about what is going on, and how to have method level annotations (this example skips that and only defines class level annotations)
I have also written up some notes on this here: http://automateddeveloper.blogspot.co.uk/2014/12/spring-mvc-custom-routing-conditions.html
Implement AuthenticationSuccessHandler onAuthenticationSuccess redirect to specific controller based on the User Role.

Asp.Net Web Api - attribute for not binding/formatting a parameter?

I have a method on an ApiController that looks like this:
public IEnumerable<Items> GetSlideSets() {
IServiceClass serviceClass = new ServiceClass();
//...
Yes, I am aware that this is not good design but I'm addressing this issue in a different iteration.
At a certain point in my application I need to call this functionality from within the project itself so I thought I could simply reuse the controller (and why not, I can pluck it out of my IoC container). The only problem is that in this case, I need to inject my own implementation of IServiceClass, easy enough:
public IEnumerable<Items> GetSlideSets(IServiceClass serviceClass = null) {
serviceClass = serviceClass ?? new ServiceClass();
//...
Except now I am getting errors when calling this via a regular Api call Optionalparameter 'serviceClass' is not supported by FormatterParameterBinding.
I know that there are various attributes that control bindings. Is there one that I can put on the parameter to say it shouldn't bind.
Like others have mentioned, it's probably a better idea to inject the dependency in the constructor.
But if you really must avoid binding an action parameter, there isn't a built-in attribute but you can create one pretty easily. Here's what it could look like:
public class DontBindAttribute : ParameterBindingAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return new DontBindParameterBinding(parameter);
}
private class DontBindParameterBinding : HttpParameterBinding
{
public DontBindParameterBinding(HttpParameterDescriptor parameter) : base(parameter)
{
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
actionContext.ActionArguments.Add(Descriptor.ParameterName, Descriptor.DefaultValue);
var completedTaskSource = new TaskCompletionSource<object>();
completedTaskSource.SetResult(null);
return completedTaskSource.Task;
}
}
}
You just need to apply the attribute to the parameter afterwards:
public IEnumerable<Items> GetSlideSets([DontBind] IServiceClass serviceClass = null)

Moq a static method in static class

public Product GetbyID(int id)
{
try
{
//mycode Product p=..........
}
catch (DataAccessException ex)
{
throw new BusinessException(ex.ErrorCode);
}
catch (Exception ex)
{
BusinessExceptionHandler.LogException(ex);
}
return p;
}
Given above is a code snippet that i need to write test cases.
here LogException(ex); is a static method in static class BusinessExceptionHandler
I have reference to Moq frame work 2.6.1014.1
How can I Moq the method BusinessExceptionHandler.LogException
I do prefer a mocking mechanism that don't need any change in method GetbyID
Moq doesn't allow the mocking of static methods so you will probably need to change the working of the static method. One option is to have the static method call an instance method of a dependency. So you'll create a "Logger" class with a Log method and add a static Logger field / property (BusinessExceptionHandler.Logger) to your static class. In the real-world scenario you can populate BusinessExceptionHandler.Logger with a standard Logger instance, using it as a Singleton. For testing, inject a Mock into the BusinessExceptionHandler.Logger and set up your expectations and verify against the mock.
Moq (and NMock, RhinoMock) will not help you here. You will have to create a wrapper class ( and virtual method ) around the LogException and use it in production code and test using that.
Or you can use a tool like TypeMock, Microsoft.Fakes etc ( http://stacktoheap.com/blog/2012/11/11/testing-extension-methods-with-microsoft-fakes/ ) if you absolutely cannot change your existing code.
Here is how I get around the problem. Say this is the class you want to unit-test:
public static class TaskFactory
{
public static T CreateTask<T>(long workRequestId, ProcessTriggerType workRequestType)
{
var task = some code to do the work;
return (T)task;
}
}
Create an interface and a wrapper class implementing it:
public interface ITaskFactoryFacade
{
T CreateTask<T>(long workRequestId, ProcessTriggerType workRequestType);
}
public class TaskFactoryFacade : ITaskFactoryFacade
{
public T CreateTask<T>(long workRequestId, ProcessTriggerType workRequestType)
{
return TaskFactory.CreateTask<T>(workRequestId, workRequestType);
}
}
Now mock out this class:
var taskFactoryFacadeMock = new Mock<ITaskFactoryFacade>();
taskFactoryFacadeMock.Setup(t => t.CreateTask<SomeTask>(It.IsAny<long>(), It.IsAny<SomeType>())).Returns(new SomeTask());
Happy Moqing.

NVelocity extension method ASP.NET webform

I was wondering if it's possible to use an extension method with asp.net webforms and nvelocity. I would like to set some defaults if the string value is null or empty.
Example of .vm file:
Example of my email body...
Billable Status: $billableStatus.Evaluate()
rest of my email body...
Attempted extension method:
public static class Helper
{
public static string Evaluate(this string value)
{
if (String.IsNullOrEmpty(value))
return "Not Provided";
else
return value;
}
}
Or is there an alternative to what I'm tryting to accomplish?
I don't think NVelocity can resolve extension methods with C#/VB.NET syntax sugar. What I do is register an instance of a helper in the velocity context:
var context = VelocityContext();
context.Put("helper", new Helper());
context.Put("billableStatus", "something");
...
and then in your template:
$helper.Evaluate($billableStatus)
You have to make your helper non-static for this to work, of course.
I came across something similar in past and I was looking for something more sophisticated and with more control. I found that NVelocity does provide a way to intercept the method and property calls but for that you will have to implement certain things. In order to make your custom interceptor you will need to implement NVelocity.IDuck. For example
public class MyClass : NVelocity.IDuck
{
public object GetInvoke(string propName)
{
....
}
public object Invoke(string method, params object[] args)
{
....
}
public void SetInvoke(string propName, object value)
{
....
}
}
Now any instance of MyClass will intercept and pass the method and property calls to our these three function implementation and give us a chance to resolve and return the output. You may notice from these three function signatures that in order to implement them we may need some reflection where we can locate respective methods on available extension types and execute them. If needed you can read following blog post for more details about going this way. NVelocity and extension methods

Resources