Disable controllers in certain API version - c#

We are developing a new API version in our application, so that we basically have a
namespace Bla.V1.Controllers
[ApiController]
[ApiVersion("1")]
public partial class SampleController : ControllerBase {}
and a
namespace Bla.V2.Controllers
[ApiController]
[ApiVersion("2")]
public partial class SampleController : ControllerBase {}
We've also configured API versioning (AddApiVersioning), so that when somebody tries to access e.g. api/v3/sample/action, he will get a version not supported error response, provided by the API versioning middleware.
Now the V2 version is in development and we want to make it available only for the development stage. The controllers code will be deployed to production, we want however to disable the V2 version for production.
We would do it with a feature flag, but what should be done to hide the V2 controllers?
I was thinking:
Somehow do not register the V2 controllers. Didn't find a suitable option in AddControllers()
Set maximum version to V1 in the API versioning middleware. This works only for headers, doesn't block access to V2 controllers
I'm currently thinking about writing an authorization attribute that would deny access based on the feature flag status and apply it to V2 controllers. Is there a better and more elegant way to do this?

Well, in case somebody is interested at some point. Our controller code is autogenerated from a Swagger definition. So I added the following code to the generation template, which gets invoked in each controller method:
if (!this.IsV2Enabled())
{
var protocol = this.Request.IsHttps ? "https" : "http";
return this.BadRequest(new
{
error = new {
code = "UnsupportedApiVersion",
message = $"The HTTP resource that matches the request URI '{protocol}://{this.Request.Host}{this.Request.Path}' does not support the API version '1'."
}
});
}

Related

Rewrite URl according to Attribute

We have an ASP.NET Core Web API running on .NET 5. It has got many controllers and routes that are sometimes protected via the Authorize attribute and sometimes they are public.
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase {
[HttpGet("me")]
public IActionResult GetMyPublicInformation()
{
// code...
}
[HttpGet("me")]
[Authorize]
public IActionResult GetMyPrivateInformation()
{
// code...
}
}
Well now I would like to publish these REST routes through different HTTP Routes, depending on the Authorization requirement. For example, the route GetPublicInformation does not require authorization. That route should be available under public/user/me. Whereas the method GetMyPrivateInformation does require authorization and should be published under the route secure/user/me.
Of coure, I am aware that I can define the route manually in the HttpGet attribute (i.e. [HttpGet("public/user/me")), but - besides that I'm lazy - it would be error prone because we have the information available twice: Once with in the manual route definition (HttpGet) and once in the Authorize attribute. So if someone forgets to change one of the two attributes, we get an inconsistency.
Question: What are my options to automate this URL rewriting (I'm thinking of middleware)? Are there any drawbacks you see using this approach? I have been fighting this idea because I don't like extra magic sauce in my codebase. I prefer explicity, that's why I'm going for the manual route naming right now...
By the way: I have to take this on me because of limitations in Microsoft's MSAL library. I believe I shouldn't have to do this because we host an OpenAPI definition on which routes are and which routes aren't authorized. That should be enough in most cases.

ASP.NET Core MVC Web API deployment issues

I'm trying to deploy my ASP.NET Core MVC Web App with Web API, i.e. I have both MVC and API controllers in the same folder.
It works fine on localhost but on IIS when I create a Virtual Directory, the path gets added to the domain.
I can find it using window.location.pathname
I can append the 'api/Get' and it works like (questions is my virtual directory)
http://example.com/questions/api/Question/GetAll
But when I navigate to other pages then then controller name also gets appended and then it causes issues.
e.g. if I navigate to the 'Question' page (QuestionController), the URL becomes
http://example.com/questions/newquestion/api/Question/Create
instead of
http://example.com/questions/api/Question/Create
How can I fix it?
Here is my Asp.Net core api.
[ApiController]
public class ScheduleController : ControllerBase
{
[HttpGet]
public List<PathologistSchedule> GetPathologistScheduleByDate(DateTime taskDate)
{
return pathologistRepository.GetPathologistScheduleByDate(taskDate).ToList();
}
}
I call this api from PathologistScheduleController's view using jquery.
Here's the error I get:
GET http://localhost:51434/PathologistSchedule/api/Schedule/?sort=&group=&filter=&taskDate=2020-11-13T21%3A16%3A47.507Z 404 (Not Found)
TIA.
A
If you have API and MVC projects in one solution you have to config your solution to run multiple projects.
You can use route attribute like this for each of your APIs
[Route("~/api/Question/GetAll")]
will give you Url http://example.com/api/Question/GetAll.
Or
[Route("~/api/Question/Create")]
will give Url http://example.com/api/Question/Create.
And it will not depend on the controller name or folder.
UPDATE because of the question update:
Use this code please:
public class ScheduleController : ControllerBase
{
[Route("~/api/Schedule/GetPathologistScheduleByDate/{taskDate?}")]
public List<PathologistSchedule> GetPathologistScheduleByDate(DateTime taskDate)
{
return pathologistRepository.GetPathologistScheduleByDate(taskDate).ToList();
}
}
for testing try this route in your browser:
http://localhost:51434/api/Schedule/GetPathologistScheduleByDate/2020-11-13T21%3A16%3A47.507Z
But basically for APIs you don't need to use any controller or action name. You can use any names you like, for example:
[Route("~/api/Pathologist/GetSchedule/{taskDate?}")]
or
[Route("~/api/GetPathologistSchedule/{taskDate?}")]
or even
[Route("~/api/{taskDate?}")]
The route just should be unique.
I added a variable in the 'appsettings.json' and 'appsettings.Development.json' called baseURL and had 'appsettings.json' set to '/VirtualDirectoryName/' and kept the one in 'appsettings.Development.json' as '/'.
Appended this variable when calling APIs.

PlatformNotSupportedException: Secure binary serialization is not supported on this platform

Trying to implement custom authorization attribute converting from WebApi 2.0 to .Net Core 3.1 and Microsoft.AspnetCore.Odata 7.4.1 on WebApi and Microsoft.Odata.Client 7.7.0.
I revamped this code from a working OData – API in .NET 4.8, and everything works until we apply the custom authorization attribute to the endpoint.
Getting the following error on the client when returning UnauthorizedResult from custom authorization attribute which is used on Odata controller. Also tried to inherit from AuthorizeAttribute instead of Attribute, IAuthorizationFilter and getting the same error.
Error:
An unhandled exception occurred while processing the request.
PlatformNotSupportedException: Secure binary serialization is not supported on this platform.
System.Exception.add_SerializeObjectState(EventHandler value)
[CustomAuthorization]
[ODataRoutePrefix("Data")]
public class DataController: ODataController
{
}
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class CustomAuthorizationAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (!ValidToken(filterContext, sUserToken))
{
filterContext.HttpContext.Response.Headers.Add("AuthorizationStatus","NotAuthorized");
filterContext.Result = new UnauthorizedResult();
}
}
}
It looks like recent work in OData Client for v7.7.0 has altered how error response are handled and how exceptions are deserialized, I would go so far as to suggest this is a bug. I can't find any direct evidence of this change in the release notes other than this issue with deserializing exceptions
Downgrading OData Client to v7.6.4 will generally resolve this.
Please contribute to this Git Hub issue #1833 that is the same underlying issue.

Kentico v8.2.12: Why is my WebAPI Route not registered correctly (usually only on the first release)?

Summary
A Kentico 8.2 website fo which I have recently implemented a Web API service isn't registering routes on first deployment and all calls return 404. Further redeployments usually fix the issue, but I would like to fix it permanently before it is released to PROD.
What is preventing the first deployment from registering the route properly?
Background
We have a Kentico v8.2.12 website that uses Web Forms using .NET Framework v4. I have registered a Web API Controller, but it appears on the first release the route isn't registered and any calls to the service returns "404 (Not Found)".
When I first deployed to the DEV environment the Web API route wasn't registered, but upon deploying another build it magically worked. One or two other releases into the DEV environment caused similar issues, but in these instances re-deploying the same build worked.
The same issue has now occurred when released to UAT, however as the deployments are carried out by another team it will be more time-consuming to re-deploy builds and looks unprofessional. I am also wary of this occurring in PROD---which may cause the live website to be down further than necessary.
Web API Implementation
The Web API Controller is inside the CMS Website project and not a separate library.
Global.asax.cs
The Global.asax.cs file's Application_Start() method registers the route and looks similar to the below:
protected void Application_Start()
{
// Scripts Bundling here, which havs been removed for brevity
BundleTable.EnableOptimizations = false;
// Registering the API
RouteTable.Routes.MapHttpRoute(
name: "DefaultApiWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
}
MyController.cs
My Controller looks similar to the below stored under the CMS website: CMSApp/ApiControllers/MyController.cs
[assembly: RegisterApiController(typeof(CMSApp.ApiControllers.MyController))]
namespace CMSApp.ApiControllers
{
public class MyController : ApiController
{
Channel Channel = new Channel();
[HttpPost]
public int Create()
{
Response objResponse = Channel.Instance.DoSomething();
HandleResponse(objResponse);
return objResponse.SessionHandle;
}
}
}
In the webbrowser, accessing /api/my/create returns a 404 (Not Found), but I expect it to tell me it's a POST method.
Lib versions [Edit, I have since updated the libs but issue still prevails]
Microsoft.AspNet.WebApi : v4.0.30506
Microsoft.AspNet.WebApi.Client : v4.0.30506
Microsoft.AspNet.WebApi.Core : v4.0.30506
Microsoft.AspNet.WebApi.WebHost : v4.0.30506
Question?
Why do the first deployments into an environment not work, but most further deployments work as I expect them to?
The issue was due to ASP.NET caching.
Once "MS-ApiControllerTypeCache.xml" was removed under "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files" and IIS was restarted, the controller was picked up.

Tell Swashbuckle to only look for controllers that have the ApiControllerAttribute

I'm working on a Web MVC project and inside it I want to have some api controllers for external apps to use.
My API controllers have the ApiController attribute on them.
Unfortunately, Swashbuckle picks up ALL the controllers/actions.
Is there a way to tell it to only look for Api controllers?
Thank you.
You can put this in the controller you want to hide from Swashbuckle
[ApiExplorerSettings(IgnoreApi = true)]
public class UserController : Controller

Categories