I have a SPA ASP.NET WebAPI application which previously allowed anonymous access. I have now configured ASP.Net Identity for it but I cannot get the Identity related controllers and my application's other controllers to work at the same time :-(
It's either one or the other!
I have added the startup class to my project:
using Test.MyProject;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;
using Owin;
using System;
using System.Configuration;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http;
[assembly: OwinStartup(typeof(Test.Client.Startup))]
namespace Test.Client
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureWebApi(httpConfig);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
//app.UseWebApi(httpConfig); // If this line is commented out my application's controllers work. But then my Account Controller does't work. It if is included, my application's controllers don't work, whilst the Account Controller work
GlobalConfiguration.Configure(WebApiConfig.Register);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
private void ConfigureWebApi(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
}
And I have added controllers for managing Users and Roles.
The statement GlobalConfiguration.Configure(WebApiConfig.Register) was previously in the application start event in global.aspx.cs but now moved to the startup class to have everything in the same place.
The WebApiConfig.Register method looks like this:
public static void Register(HttpConfiguration config)
{
var container = new UnityContainer();
// Web API configuration and services
string appStorageProvider = ConfigurationManager.AppSettings["StorageProvider"];
var provider =(TestComposition.StorageProvider) Enum.Parse(typeof (TestComposition.StorageProvider), appStorageProvider, true);
TestComposition.Setup(container, provider);
container.RegisterType<GeneralLogger, GeneralLogger>();
container.RegisterType<IExceptionLogger, ExceptionLogger>();
config.EnableCors();
config.DependencyResolver = new UnityResolver(container);
config.Services.Add(typeof (IExceptionLogger), container.Resolve<GeneralLogger>());
// Web API routes
config.MapHttpAttributeRoutes();
}
In my new AccountController I have code which allows me to retrieve the ApplicationUserManager from the OwinContext set up in the Startup class.
protected ApplicationUserManager AppUserManager
{
get
{
return _AppUserManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
With app.UseWebApi(httpConfig) commented out as shown above my application works as it used to. But if I invoke any action on my new AccountController I get this:
Request.GetOwinContext() error CS1061: 'HttpRequestMessage' does not
contain a definition for 'GetOwinContext' and no extension method
'GetOwinContext' accepting a first argument of type
'HttpRequestMessage' could be found (are you missing a using directive
or an assembly reference?)
If I comment in the app.UseWebApi(httpConfig) statement the AccountController works but then my other controllers don't work. Here I get errors like these:
{
"message": "An error has occurred.",
"exceptionMessage": "An error occurred when trying to create a controller of type 'TestController'. Make sure that the controller has
a parameterless public constructor.",
"exceptionType": "System.InvalidOperationException",
"stackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)\r\n at
System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage
request)\r\n at
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()",
"innerException": {
"message": "An error has occurred.",
"exceptionMessage": "Type 'MyProject.Api.TestController' does not have a default constructor",
"exceptionType": "System.ArgumentException",
"stackTrace": " at System.Linq.Expressions.Expression.New(Type type)\r\n at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType)\r\n at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage
request, Type controllerType, Func`1& activator)\r\n at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)"
} }
Any idea what is going on here?
The problem is that you are not using the same HttpConfiguration instance during start up for configuring WebApi with OWIN.
This way your OWIN Web API middleware has no knowledge of UnityContainer, and will use its default implementation. Because of this the creation of your controllers failed.
Please use the same HttpConfiguration for both Web Api configuration and UnityContainer registration:
public class Startup {
public void Configuration(IAppBuilder app) {
ConfigureOAuthTokenGeneration(app);
ConfigureWebApi(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app) {
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
private void ConfigureWebApi(IAppBuilder app) {
// configure Web Api
GlobalConfiguration.Configure(WebApiConfig.Register);
// Manually assign httpConfig from GlobalConfiguration
HttpConfiguration httpConfig = GlobalConfiguration.Configuration;
// Use same config with OWIN app
app.UseWebApi(httpConfig);
}
}
You are configuring web api in multiple places. The WebApiConfig.Register method should consolidate everything you want configured for the HttpConfiguration:
public static void Register(HttpConfiguration config) {
var container = new UnityContainer();
// Web API configuration and services
string appStorageProvider = ConfigurationManager.AppSettings["StorageProvider"];
var provider =(TestComposition.StorageProvider) Enum.Parse(typeof (TestComposition.StorageProvider), appStorageProvider, true);
TestComposition.Setup(container, provider);
container.RegisterType<GeneralLogger, GeneralLogger>();
container.RegisterType<IExceptionLogger, ExceptionLogger>();
config.EnableCors();
config.DependencyResolver = new UnityResolver(container);
config.Services.Add(typeof (IExceptionLogger), container.Resolve<GeneralLogger>());
// Web API routes
config.MapHttpAttributeRoutes();
// configure formatter
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
Related
I can't reach any methods from my ApiController in anyway, the routing does appear if i try to reach it by a browser but no methods are shown.
My Controller:
namespace AgroRiego.Controllers
{
public class datacontrol : ApiController
{
[HttpGet, Route("api/get")]
public string Get([FromUri]string user, string pass)
{
string check = SQL.Reader("SELECT * FROM users WHERE username='" + user + "' AND password='" + pass + "'");
if (String.IsNullOrWhiteSpace(check))
{
return "error en credenciales";
}
DataTable horarios = SQL.table_read("SELECT * FROM horario_riego");
string json = Utils.ConvertDataTabletoJSON(horarios);
return json;
}
[HttpPost, Route("api/post")]
public void Post([FromBody]string value)
{
string i = value;
}
}
}
my global asax:
namespace AgroRiego
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
and my webapiconfig:
namespace AgroRiego
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Configuración y servicios de API web
// Rutas de API web
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
i have more webforms inside the project (originally it was just html pages with serverside code, but i need to add a couple methods to retrieve and send data, help much appreciated!
EDIT1: i managed to reach HTTP 200 changing the URL but i can't reach the methods anyway (in debug mode it does not stop on the breakpoints) how can i route correctly the Api (so it is not Login.aspx) and how do i fix the methods reaching?
EDIT2: i read in documentation that i need this line in global:
RouteConfig.RegisterRoutes(RouteTable.Routes);
but im not using MVC does that matter? i tried reaching the routes with a brand new MVC Web Api and it yields "No Response"
use a routerprefix with your controller. So you access the URL as
http://localhost/routerprefix/router
HttpClient class can be use to send and receive HTTP requests and responses. Since you are trying to consume a WebApi from a aspx page, better way is to create a HttpClient instance
Below is a very simple implementation. Please check this url for further information
MSDN sample
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:49342/api/get");
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync();
}
By the look of your set up, it seems correct
you have got:
config.MapHttpAttributeRoutes(); - setup the attribute route
config.Routes.MapHttpRoute( - setup the default route
GlobalConfiguration.Configure(WebApiConfig.Register); - to register at startup
so it should work.
I think the problem you are having is the way you are calling it
WebAPI routing work a little different to MVC
for example:
In you get method, the route is set as below
[HttpGet, Route("api/get")]
so you should call it {host}/api/get using a GET http method
in the screen shot, you are calling using {host}/api/get/Get - which would not have work, because no route would match
Same for your POST method
So give it another try and you should be able to reach it
The URL to add in the rest testing tool is
http://localhost:49342/api/get
Method type is GET
If you are calling this web api from aspx page use the httpClient class.
An error occurred when trying to create a controller of type
'ChatBotController'. Make sure that the controller has a
parameterless public constructor.
at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType) ↵ at
System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage
request) ↵ at
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__15.MoveNext()
When I try to reach my IFeedbackRepository I get the error aboe. It happens when I put in the constructor in my ChatBotController.cs
public class ChatBotController : ApiController
{
IFeedbackRepository _feedbackRepository;
public ChatBotController(IFeedbackRepository feedbackRepository)
{
_feedbackRepository = feedbackRepository;
}
[HttpPost]
public IHttpActionResult PostQuestion([FromBody]string message) //TODO: make sure that webapi will search the message in the body of the http request
{
throw new NotImplementedException();
}
}
I'm using both MVC and Api which I both resolve in my Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DependencyConfig.RegisterWebApiDependencies();
DependencyConfig.RegisterMvcDependencies();
}
This is my DependencyConfig.cs for both MVC and Api:
public static void RegisterWebApiDependencies()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IAnswerGenerator, PxlAnswerGenerator>(Lifestyle.Scoped);
container.Register<ChatBotDbContext>(Lifestyle.Scoped);
container.Register<IFeedbackRepository, FeedbackDbRepository>(Lifestyle.Scoped);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
public static void RegisterMvcDependencies()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.Register<IFeedbackRepository, FeedbackDbRepository>(Lifestyle.Scoped);
container.Register<ChatBotDbContext>(Lifestyle.Scoped);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
What am I doing wrong?
According to the documentation of Simple-Injector when you want to initialize the resolver for the WebApi part of your registration you need to call
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorWebApiDependencyResolver(container));
I don't see you calling container.RegisterWebApiControllers(GlobalConfiguration.Configuration); in RegisterWebApiDependencies(). This is required.
You may want to review the simpleinjector documentation for integrating with ASP.NET Web API and MVC here:
https://simpleinjector.readthedocs.io/en/latest/webapiintegration.html
Also the documentation above has the container/DI setup at the beginning of application_start(). If the above change alone does not work, you may want to try putting the following two lines at the start of application_start():
DependencyConfig.RegisterWebApiDependencies();
DependencyConfig.RegisterMvcDependencies();
I am working on an asp.net MVC-5 web application, and using nuget i installed the hangfire tool:-
Install-Package Hangfire
but when i run my application i got this exception:-
The following errors occurred while attempting to load the app.
- No assembly found containing an OwinStartupAttribute.
- No assembly found containing a Startup or [AssemblyName].Startup class.
To disable OWIN startup discovery, add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
To specify the OWIN startup Assembly, Class, or Method, add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.
second question. if i got the above error fix, how i can call an action method on predefined intervals using hangfire. currently i am defining this inside my glabal.asax as follow:-
static void ScheduleTaskTrigger()
{
HttpRuntime.Cache.Add("ScheduledTaskTrigger",
string.Empty,
null,
Cache.NoAbsoluteExpiration,
TimeSpan.FromMinutes(60)),
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback(PerformScheduledTasks));
}
static void PerformScheduledTasks(string key, Object value, CacheItemRemovedReason reason)
{
//Your TODO
HomeController h = new HomeController();
var c = h.ScanServer("12345", "allscan");
ScheduleTaskTrigger();
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
ScheduleTaskTrigger();
}
----EDIT----------
now after adding the startup.css class , i defined the following inside my global.asax :-
HomeController h = new HomeController();
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
// ScheduleTaskTrigger();
RecurringJob.AddOrUpdate(() => h.ScanServer("12345","allscan"), Cron.Minutely);
}
mainly to call an action method named "ScanServer" under the Home controller. now the ScanServer is an async task which have the following defenition :-
public async Task<ActionResult> ScanServer(string tokenfromTMS, string FQDN)
{
so my global.asax is raising this error :-
Async methods are not supported. Please make them synchronous before using them in background.
It seems that your OWIN startUp class is missing, So create a class with name Startup:
public class Startup
{
public void Configuration(IAppBuilder app)
{
//..codes
}
}
For your second question, if you want to call a method, for example each hour you can use RecurringJob:
RecurringJob.AddOrUpdate(() => CallMethod(), Cron.Hourly);
I have started playing around with ASP.NET 5 vNext and I am struggling passing the options from config.json into a middle-ware service that is used by my WebApi controller.
Here is a snippet with my middle-ware service:
public class MyService : IMyService
{
public MyService(IOptions<MyOptions> settings)
{
var o = settings.Options;
}
}
Here is my WebApi controller that is using the middle-ware service:
public class MyController : Controller
{
private IMyService _myService;
public TestController(IMyService service)
{
_myService = service;
}
}
In Startup.cs I am reading the options:
services.AddOptions();
services.Configure<MyOptions>(Configuration);
What I am struggling with is how to register an instance to IMyService so that it would be passed to the constructor of the controller (how can I get a hold of the IOptions)?
services.AddInstance<IMyService>(new MyService(XXXXX));
As suggested below I did try to use both
services.AddTransient<MyService>();
and
services.AddSingleton<MyService>();
But in both cases I am seeing the following error:
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type
'MyApp.Services.IMyService' while attempting to activate
'MyApp.Controllers.TestController'.
Microsoft.Framework.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
Thanks for your help!
Don't register it as an Instance. Instead just add it as Scoped/Transient/Singleton depending on your requirements and let Dependency Injection do its magic;
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddScoped<IMyService, MyService>();
For example, you can to add to Startup.cs that code:
public void ConfigureServices(IServiceCollection services)
{
var builder = new ConfigurationBuilder("[path to file with configuration]");
builder.AddJsonFile("config.json");
var config = builder.Build();
services.AddOptions();
services.Configure<MyOptions>(config);
//services.AddSingleton<IMyService, MyService>();
services.AddTransient<IMyService, MyService>();
}
I can assure you that you can use Singleton or Transient.
If you're interested, you can find more info here https://github.com/aspnet/Docs/issues/24.
And additionally, currently Autofac creates DI for ASP.NET 5 on
http://alexmg.com/autofac-4-0-alpha-1-for-asp-net-5-0-beta-3/
I want to access full capabilities of SimpleMembershipProvider such as ValidateUser method.
So according to its documentation I should not call WebSecurity.InitializeDatabaseConnection() for initialization and instead enable standard membership and role providers.
My question is: How can I initialize SimpleMembershipProvider class
To Finally: have access to full capabilities of SimpleMembershipProvider
or if there is a better solution, thanks
How can I initialize SimpleMembershipProvider class
If you look at the default ASP.NET MVC 4 Internet application template the AccountController is decorated with the [InitializeSimpleMembership] attribute. That's how it is initialized in this sample. This means that you will be able to use it once you have gone through the account controller, not before. If you want to use your membership provider before authenticating you could do the same in your Application_Start method.
If you were to merge the InitializeSimpleMembershipAttribute into the Global.asax.cs Application_Start so that the SimpleMembershipProvider would be initialized without any AccountController routes being called...
...it could look something like this: http://aaron-hoffman.blogspot.com/2013/02/aspnet-mvc-4-membership-users-passwords.html
// The using below is needed for "UsersContext" - it will be relative to your project namespace
using MvcApplication1.Models;
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Threading;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using WebMatrix.WebData;
namespace MvcApplication1
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
Database.SetInitializer<UsersContext>(null);
try
{
using (var context = new UsersContext())
{
if (!context.Database.Exists())
{
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
}