I am working on Web API project under .NET Framework 4.6 currently.
It uses bearer token authentication.
But I have noticed the issue with response time of controllers' actions. The response time is quite big even Web API is hosted on local IIS Express. Namely the logging (based on IActionFilter) shows the execution time of the controller is 20 milliseconds, meanwhile Postman shows the response time is about 3 or 4 seconds.
What can be the reason of such difference?
Two steps were taken:
to use the extension method SuppressDefaultHostAuthentication in order to avoid possible side effect from a default authentication. No improvements unfortunately.
to add the dependency injection the default implementation of interfaces which were missing initially and respective exceptions were thrown on Web API start. Namely I have added
.RegisterType<IHttpControllerSelector, DefaultHttpControllerSelector>()
.RegisterType<IHttpActionSelector, ApiControllerActionSelector>(). No improvements unfortunately.
Please find below the content of WebApiConfig.cs and Startup.cs files
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.MapHttpAttributeRoutes();
//config.SuppressDefaultHostAuthentication();
// TODO: check the necessity to use Storages here, why not on services level
var container = new UnityContainer();
/*some dependecies mapping here*/
container.AddExtension(new Diagnostic());
config.DependencyResolver = new UnityResolver(container);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Filters.Add(new ApiAuthenticationFilter(container.Resolve<BLI.IUserSessionManagement>()));
config.Filters.Add(new ApiAuthorizationFilter(container.Resolve<BLI.IAuthorizer>(), container.Resolve<BET.IAuthLogger>()));
config.Filters.Add(new LoggingFilterAttribute(new BET.ControllerTracer()));
}
Startup.cs file
public void Configuration(IAppBuilder app)
{
//TODO : try to find better solution
BackEnd.WebAPI.Models.UnityResolver ur = (BackEnd.WebAPI.Models.UnityResolver)System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
Type providerType = Type.GetType("Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider, Microsoft.Owin.Security.OAuth", true);
ApiOAuthAuthorizationServerProvider serverProvider = (ApiOAuthAuthorizationServerProvider)ur.GetService(providerType);
//
OAuthAuthorizationServerOptions oAuthOptions = new OAuthAuthorizationServerOptions()
{
TokenEndpointPath = new PathString("/auth"),
Provider = serverProvider,
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
AllowInsecureHttp = true
};
app.UseOAuthAuthorizationServer(oAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
additionally taken actions:
disable authentication and authorization filters. No improvement detected.
perform the same tests on Azure. The same situation: logging based on action filter reports high performance of contoller actions, but client receives responses with essential delay as on local IIS Express
The reason was found.
It was due to too complex controller constructor.
The revision has solved the issue
Related
I have a project which I created via File > New > Project > Azure Mobile App
I've added routes to my HttpConfiguration in the ConfifgureMobileApp() method.
The code looks like this. Not in particular the line config.Routes.MapHttpRoute(...).
private static void ConfigureMobileApp(IAppBuilder app, ContainerBuilder builder)
{
var config = new HttpConfiguration();
builder.RegisterModule(new WebApiModule(config));
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
//For more information on Web API tracing, see http://go.microsoft.com/fwlink/?LinkId=620686
config.EnableSystemDiagnosticsTracing();
config.Filters.Add(new CultureThreadingAttribute());
config.Routes.MapHttpRoute("DefaultApi", "api/{culture}/{controller}/{id}", new { id = RouteParameter.Optional });
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.MapApiControllers()
.ApplyTo(config);
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
if (string.IsNullOrEmpty(settings.HostName))
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
}
app.UseAutofacWebApi(config);
app.UseAutofacMiddleware(container);
}
But this route is not getting picked up. In my middleware, I've taken the {System.Web.Routing.RouteData} object from the OwinContext.Environment dictionary, and it contains no routes.
I am using https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-dotnet-backend-how-to-use-server-sdk, and in particular, I'm following the advice of that article by mapping the route before making the call to ApplyTo(config).
Also, my action filter CultureThreadingAttribute, which changes the locale of the thread based on the culture coming in from the route, is failing to get picked up.
I suspect that there are multiple HttpConfiguration objects running around in my app. Is there a way to fix this?
The route for your culture and the route for MobileApps APIs are conflicting with one another likely. Debugging the route handler will confirm it. Set a breakpoint and take a look at the route table as a call comes in.
There is nothing magical about the [MobileAppController] attribute - it just enforces the ZUMO-API-VERSION. You can remove the .MapApiControllers() element and use regular ASP.NET MVC routing for your API controllers. This will simplify your API route definitions.
I have created a webAPI project and I would like to start hosting it in IIS.
The method in the controller looks like this:
[TokenAuthentication]
[HttpGet]
[Authorize(Roles=Roles.PING)]
[Route("~/Ping")]
public DateTime Ping()
{
return DateTime.UtcNow;
}
My WebApiConfig looks like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
GlobalConfiguration.Configuration.Filters.Add(new ValidateModelAttribute());
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
}
}
When I run the project via visual studio, and go to the link http://localhost:53722/ping I get an unauthorized response which is what I expect.
When I put the website on IIS in an applicaion with the virtual path "/Account" I get a 404 response and it is trying to use the StaticFile Handler.
Through searching online I have runAllManagedModulesForAllRequests set to true. I have also found Configure ASP.NET Web API application as a Virtual Directory under ASP.NET MVC application which seems like the "/Account/" should not be interfering with the route.
I can not put the API at the route directory because I will be running multiple microservices under the same site.
How would I get the route to work using the virtual directory?
Thanks,
Steven
Each site should be running as an Application and not a Virtual Directory (and ideally with each having its own application pool).
Out of the box you would normally go for
[Route("ping")]
This would give you a url of http://blah.com/ping
If you are running this api in a sub folder/application then it maybe something like http://blah.com/subapp/ping
This is all i have...
G'day!
I haven't seen much on this because its all very new at the time of this writing. I am trying to write a service fabric application that serves a web app (html/js) after the user has been authenticated via ACS. I can easily get this to work with OWIN in a non service fabric environment, i.e. a traditional Asp Net application behind IIS. I'm trying to use token authentication with Azure Access Control.
So something to do with the fact that I am now using service fabric has changed the way OWIN works? Below is my OWIN ConfigureApp() function within my Startup.cs in my service fabric application:
public static void ConfigureApp(IAppBuilder appBuilder)
{
appBuilder.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions());
appBuilder.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = _realm,
MetadataAddress = _acsXmlMetaDataUrl
});
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
Notice how I inject the WsFederation middleware before the web api middleware that will eventually be used to serve my browser html/js application. Now when this launches and I do a sanity test like navigating to a REST url my content is served instantly rather than being redirected to Azure Access Control to sign in and get an auth token. In my traditional Asp Net application with the same OWIN configuration I am indeed redirected to Azure Access Control before any resources are served.
So my question is how do I inject WsFed middleware into the OWIN pipeline such that this will work in a service fabric context?
Any assistance would be much appreciated, thank you for your time!
I don't know why this code is working for MVC and not for Service Fabric. I had the same issue, but I found a way to make it work for SF.
This article gives a tutorial.
Basically, in your code, you're not telling it to authenticate. You're setting everything up but you're not starting it.
app.Map("/login", map =>
{
map.Run(async ctx =>
{
if (ctx.Authentication.User == null ||
!ctx.Authentication.User.Identity.IsAuthenticated)
{
ctx.Response.StatusCode = 401;
}
else
{
ctx.Response.Redirect("/");
}
});
});
app.Run(async ctx =>
{
var user = ctx.Authentication.User;
var response = ctx.Response;
response.ContentType = "text/html";
if (user != null && user.Identity.IsAuthenticated)
{
await response.WriteAsync(string.Format("<h2>{0}</h2>",
user.Claims.First().Issuer));
await response.WriteAsync("<dl>");
foreach (var claim in user.Claims)
{
await response.WriteAsync(string.Format(
"<dt>{0}</dt> <dd>{1}</dd>",
claim.Type,
claim.Value));
}
await response.WriteAsync("</dl>");
}
else
{
await ctx.Response.WriteAsync("<h2>anonymous</h2>");
}
});
When you're accessing a link on your website, the code in app.Run starts executing to check if you're logged in. If you're not, in this case, it will write "anonymous" on the page instead of loading your content. To authenticate, go to yourwesite/login and it will redirect you to whatever auth provider you have in the configuration.
Conclusion: add the login, logout and app.Run snippets, give it a final tweak if you have to and that should be it.
I'm not sure if this is a question that should be asked on SO, but I can't think of a better place to ask it. What I want to know is: Does anyone out there actually use the Web API 4.5.2 Template that ships with Visual Studio. I've been writing C# Web Applications for some time now, and I feel like these templates are bloated nightmares to work with. They also seem to push developers away from understanding how things are actually working under the hood. Do you agree or disagree, and should I be using these templates if I want to call myself a .Net Web Developer?
This is ofcourse highly opinionated, but yes I agree; the default (MVC related) templates are bloated with unnecessary features and there's nothing lean and mean about them. I've used a custom template for a long time just to get around this (frustrating) problem.
Note that Visual Studio now has the option to create an empty website and gives you the option to only include WebAPI, not forcing you to include ASP.NET MVC. This produces a nice tiny project.
If you do experiment alot with WebAPI and you don't want your harddisks to be a cluster bomb full of Visual Studio projects, I can highly recommend LinqPad. With a tiny bit of code you can create a self hosting process and use it as a template. For example:
#define NONEST
void Main()
{
string baseAddress = "http://localhost:9000/";
try
{
// Start OWIN host
using (WebApp.Start<Startup>(url: baseAddress))
{
// Create HttpCient and make a request to api/values
HttpClient client = new HttpClient();
var response = client.GetAsync(baseAddress + "api/values").Result;
Console.WriteLine("response: " + response);
Console.WriteLine("result: " + response.Content.ReadAsStringAsync().Result);
}
}
finally
{
// LinqPad keeps the AppDomain running to reduce compile time.
// Force app domain unload (Displays "Query ended unexpectedly")
// You can also press shift-F5 to unload the AppDomain.
AppDomain.Unload(AppDomain.CurrentDomain);
}
}
// Define other methods and classes here
public class Startup
{
// This code configures Web API. The Startup class is specified as a type
// parameter in the WebApp.Start method.
public void Configuration(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
}
}
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
I have an existing .NET 4 console application which I want to start exposing a REST API. I'm using the Microsoft self host Web API library to have it expose that API, but I'm having trouble understanding how the routing path gets developed.
Here is an example of a controller, which should expose some database objects my console application already handles:
public class UserController : ApiController
{
UserInformation[] users;
public IEnumerable<UserInformation> GetAllUsers()
{
// snip.
}
public UserInformation GetUserById(int id)
{
// snip.
}
}
And I'm exposing my API in Program.cs like so:
var config = new HttpSelfHostConfiguration("http://localhost:8800");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config)) { // code }
Given the above code, I'd expect that I could get resources by making http requests like http://localhost:8800/api/users and http://localhost:8800/api/users/1, but those don't seem to work. How does the controller part of the GET request get created? It doesn't seem like users is the correct routing path for the API, but I'm not sure what goes there.
Thanks for any help
That's because your controller is called UserController and not UsersController. Either rename your controller to UsersController or modify your request to go to http://localhost:8800/api/user.
This should solve the problem.