ASP.Net Web API - Extract header in common place - asp.net

I am working on SaaS application where I have implemented ASP.Net Web API as a service layer. In my Web API, when any request generated, it will have one header value "x-companyid" which is company specific and identify request comes from which company (Tenant).
I require that companyID in all my ApiControllers in Web API project. Of course I can get header value in every ApiController by using "Request.Headers.GetValues("x-companyid") but it will be repeated in all ApiControllers.
I have tried to create "BaseApiController" and inherit all my ApiControllers from "BaseApiController" but it's not allowing me to override ActionExecuting so that I can extract header at common place.
Can anyone suggest how can I extract "x-companyid" header commonly in my project so that I don't have to repeat code in all ApiController?

Your Best friend are action filters.
as they intercept each api call for specific controllers.
All u had to do is to decorate the controler name with something u made like
[Tenant]
public foo MyController()
{}
this approach gives you controll over what controllers you wish to add this extract to happen becouse maybe youll need some lookup apis and stuff that dont need to be for a specific Tenant
here is a very Helpful link :
https://www.c-sharpcorner.com/article/filters-in-Asp-Net-mvc-5-0-part-twelve/
Update
since there is a only one company for each instance
i would recommend adding a singleton for the current tenant u can access that anyway
https://en.wikipedia.org/wiki/Singleton_pattern

Related

Service Fabric Web API Versioning issue

I'm working on a service fabric project with multiple stateless services. When i try to add versioning as in the code below
[Authorize]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class SessionController : Controller
{
...
}
it's not working when calling the service later using Postman or using some client winforms app i made just to call this service. And when i say it's not working i mean it's not looking for a specific version i placed in the controller.
e.g.
I'm calling http://localhost:1234/api/v1.0/session/set-session and as you can see in my controller i only have version 2.0. Now my API gets hit this way or another no matter what version number i put in.
I added code to the Startup.cs
services.AddApiVersioning(options => {
options.DefaultApiVersion = new ApiVersion(2, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
});
Specific API call looks like this:
[HttpPost]
[Route("set-session")]
public async Task<IActionResult> SetSession([FromBody] SessionModel model)
{ ... }
Can anyone tell me what am i missing or maybe api versioning is not supported in service fabric at all?
Thanks.
Does your solution work locally? Based on what I see, I would suspect - no. This should have nothing to do with Service Fabric at all.
Issue 1
I see that your base class inherits from Controller, which is allowed, but is usually ControllerBase. No concern there, just FYI. The crux of the problem is likely that your controller has not applied the [ApiController] attribute. API Versioning defines IApiControllerSpecification and IApiControllerFilter, which is used to filter which controllers should be considered an API. This is important for developers building applications that have the UI and API parts mixed. A controller is a controller in ASP.NET Core and it was difficult to distinguish these two in the early days. There is now a built-in IApiControllerSpecification that considers any controller with [ApiController] applied to be an API. This can be changed, replaced, or completely disabled using ApiVersioningOptions.UseApiBehavior = false.
If your library/application is only APIs, you can decorate all controllers at once using:
[assembly: ApiController]
Since your controller is not currently being considered an API, all requests matching the route are being directed there. The value 1.0 is being considered an arbitrary string rather than an API version. This is why it matches at all instead of HTTP 400. I suspect you must only have one API and it is defined as 2.0; otherwise, I would expect an AmbiguousActionException.
Issue 2
Your example shows that you are trying to version by URL segment, but you've configured the options to only consider the header x-api-version. This option should be configured with one of the following:
URL Segment (only)
options.ApiVersionReader = new UrlSegmentApiVersionReader();
URL Segment and Header
// registration order is irrelevant
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"));
Default (Query String and URL Segment)
// NOTE: this is the configuration
// options.ApiVersionReader = ApiVersionReader.Combine(
// new QueryStringApiVersionReader(),
// new UrlSegmentApiVersionReader());
Side Note
As defined, using the URL segment and header versioning methodologies don't make sense. You have a single route which requires an API version. A client will always have to include the API version in every request so there is no point to also supporting a header.
If you define 2 routes, then it makes sense:
[Route("api/[controller]")] // match by header
[Route("api/v{version:apiVersion}/[controller]")] // match by url segment
Versioning by URL segment, while common, is the least RESTful. It violates the Uniform Interface constraint. This issue demonstrates yet another problem with that approach. Query string, header, media type, or any combination thereof will all work with the single route template of: [Route("api/[controller]")]
Observation 1
You have configured options.AssumeDefaultVersionWhenUnspecified = true. This will have no effect when versioning by URL segment. It is impossible to provide a default value of route parameter in the middle of a template. The same would be true for api/value/{id}/subvalues if {id} is not specified.
This option will have an effect if you:
Add a second route template that doesn't have the API version parameter
You update your versioning strategy to not use a URL segment
It should be noted that is a highly abused feature. It is meant to grandfather in existing services that didn't previously have explicit versioning because adding it will break existing clients. You should be cognizant of that if that isn't your use case.

Handle cookie in Spring MVC

I have an Spring MVC application which using the classic three layer: controller service and dao.
And the related models in the application contains User Department Project.
One user will belong to a department, and there may be a lot of projects belong to a certain department, and the departments are organized as a tree like structure, for example:
dep1
dep1-1
dep1-1-1
dep1-1-2
...
...
...
Now I have a controller to list the projects:
class ProjectController{
private ProjectService projectService;
#RequestMapping("/list")
public String list(#RequestParameter("depId") String depId){
projectService.list(depId);
return "list";
}
}
ProjectServiceImpl implements ProjectService{
ProjectDao projectDao;
public List<Department> list(String depId){
}
}
It seems that this is rather simple, however we have two problems:
1 The result filter.
According to the configuration, the department of the current user maybe(or not) be under consideration during the query operation, for example, when the parameter depId is dep1-1-1, and the current user belongs to dep1-1-2, then we should return null.
As said, this feature maybe closed at all at some situation.
2 The authentication.
The user authentication and management is served in another application which will deployed at the same domain with my application, you can think they are two different folder inside the /tomcat/webapps. We use cookie to share the user information:save a token for a user.
Which means for every request, I will have to get the token of the current user(if they have login) from the cookie, and then call the service provided by the other application to get the information like department and etc.
So where to do the department check, in controller or service? I am not sure if inject the HttpRequest to service is a good idea or not.
Also Since there are too many controllers and services related to this kind of operation, I want to avoid the duplicate codes everywhere.
Is there any better choices?
It may be overkill, but what you are asking for make me think to spring-security :
authentication : spring security could do it directly, but you can also implement a custom PreauthenticatedAuthenticationFilter that would use the cookie to get the user info and populates a Spring Security Authentication token. This part is easy and highly configurable.
result filter : extract from the spring security reference manual :
Spring Security supports filtering of collections and arrays and this can now be achieved using expressions. This is most commonly performed on the return value of a method. For example:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
When using the #PostFilter annotation, Spring Security iterates through the returned collection and removes any elements for which the supplied expression is false
All this is implemented through Spring AOP so it is easy to add those annotations on service methods.
Spring Security integrates nicely in a Spring MVC application. The only problem is that a full configuration including domain object security is not really trivial ...

Spring - Getting domain name in use

I have a Spring application running that uses SpringMVC and EclipseLink.
This application works well with multiple top level domains at the same point, for example you can access:
http://www.foo.com/user/list
http://www.anotherdomain.com/user/list
http://www.company.com/user/list
But now I need to load a customized design for each domain, so I need to identify what is the domain that's accessing.
I'll need to implement a class that would set views path, etc.
Anyone knows a good solution for this?
Get it from Header "Host". In Spring
you can try #RequestHeader String host in controller method as parameter.
OR
get it from HttpServletRequest.getHeader("host")

Restful Web API from Browser

I am using ASP.NET MVC 4 WEB API to create a Restful API service. This is my first go at it, so if you feel I am taking a wrong approach please feel free to correct.
I want to create a rest API (only & not a website, the consumer of the api can decide where they want to consume it), in the past I have used Restful WCF service to achieve this.
I have created a new ASP.NET MVC 4 Web Application and chose the WebAPI project template. I have added a controller class 'CatalogueController.cs' the purpose is on Get() operation I want to return the Catalogue list. The CatalogueDo contains only one property 'Service' of type string.
[System.Web.Http.HttpGet()]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, Catalogue);
}
When I run the application the browser loads with the URL http://localhost:5502/ resource not found, if I add the controller name http://localhost:5502/Catalogue/ the browser pops open a notepad with,
[{"Service":"Exchange"},{"Service":"Holidays"}]
The data is correct but
the browser keeps showing resource not found and after my request has been served the URL changes to http://localhost:5502/.
Question,
Am I doing something wrong? Should the response that pops up in the
notepad not be shown as xml in the browser it self?
Why does the controller name get removed from the URL once the request has been served?
Is it at all possible to invoke this REST service from Excel or Power Pivot?

How Do I Get RouteData Values from a Web Service in .Net 4.0

I am trying to extract an id number from a URL using a web service so that it can be used as a parameter for a where clause in a select statement that produces data from a database based on the id number of a record. That data will then be passed back to the page to populate an element in a jQuery modal popup widow.
Everything works fine with a static id number (ex: string postid = "120"), but I don't know how to get the id number from the URL. I'm using Routing in .Net 4 and the method for accessing Routing in pages does not work in a web service. In pages I just do stuff like var id = RouteData.Values["id"]; and that gets the id, but when i did it in a web service I got an error:
CS0120: An object reference is required for the non-static field,
method, or property 'System.Web.Routing.RouteData.Values.get'
Summary:
I have web service accessed form a details page where I want to get RouteData for the page making the request. I want to do this just as easily as I can on a page using RouteData.Values which is just as easy as the now the obsolete Request.Querystring.
Now I am more confused because although I could easily add a new route for the web service I don't know I would call that using jQuery Ajax because of the webservice.asmx/webmethod syntax.
Right now I have URL: "../webservices/googlemaps.asmx/GetGoogleMap" in my jQuery Ajax, but that is not a real URL. It only exists in jQuery somewhere and the way to call the service using just JavaScript is no a real URL either, its webservice.webmethod() which in this case would be googlemaps.GetGoogleMap().
I will try registering a route for webservices/googlemaps.asmx/GetGoogleMap/postid, but I doubt it will work because GetGoogleMap is not a directory or a querystring.
Get current http request and use RequestContext property to get request context - it has current routing data. For example,
var id = HttpContext.Current.Request.RequestContext.RouteData.Values["id"];
In case of WCF based web service, make sure that service is participating in ASP.NET pipeline (see ASP.NET Compatibility)
EDIT: Sorry for misleading answer - the above will not work unless web service url is registered in routing engine. However, it may not solve your issue of retrieving the id - what kind of service implementation are you using? Are you making a GET request or POST request? Typically, web service handler (asmx) or WCF pipeline should convert GET/POST parameters to method parameters. Post your web service code and how you invoke it.

Resources