For this current project I am working on, we need to implement a web api. It needs to live inside the existing webforms project. And the specifications say we need to use Owin.
So after wiring everything up using: Microsoft.Owin, Microsoft.Owin.Host.SystemWeb, Microsoft.Owin.Hosting, Microsoft.Owin.Security
A proper startup class with the OwinStartupAttribute.
app.UseWebApi with a windsor IOC container.
Web api seems to work as expected.
Except for the fact that all requests made to the existing website also go through to webapi.
A bit more explanation.
We needed a LanguageMessageHandler : DelegatingHandler. After setting that class up we've started noticing that the breakpoint on 'SendAsync gets caught even when we are not requesting anything webApi related.
The older website shouldn't even have knowledge about this handler.
A bit code the clarify:
The startupclass:
[assembly: OwinStartupAttribute(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = ((IContainerAccessor)HttpContext.Current.ApplicationInstance).Container;
app.UseWebApi(container);
}
}
The UseWebApi extension:
public static void UseWebApi(this IAppBuilder app, IWindsorContainer container)
{
var config = new HttpConfiguration
{
DependencyResolver = new WindsorDependencyResolver(container)
};
//Web API Routes
config.MapHttpAttributeRoutes();
//Default to json when requested by browser
config.Formatters.JsonFormatter.MediaTypeMappings.Add(new RequestHeaderMapping("Accept", "text/html", StringComparison.InvariantCultureIgnoreCase, true, "application/json"));
//Add language handler
config.MessageHandlers.Add(new LanguageMessageHandler());
//Ensure initialized
config.EnsureInitialized();
//Start WebApi
app.UseWebApi(config);
}
So now we are trying to figure out why all the requests are handled by the LanguageMessageHandler and not just the requests that are made for webApi.
An example route:
[RoutePrefix("api/dossier")]
public class AdministrationsController : ApiController
{
//GET
[Route("{idtype}_{id}/administrations/planned/")] //?limit={maxdate}&nursingunit={nuid}
[HttpGet]
public IHttpActionResult Planned(string idtype, int id, [FromUri] int maxdate = 6, [FromUri] int? nuid = null)
{
return Ok();
}
}
Fixed by using a filter instead of a message handler.
Was wrongfully asuming that message handler was going to be executed after routing in the pipeline.
Related
I'm moving some of internal projects from NET Core 2.0 to 3.0, and having trouble with getting the controller to execute after middleware has finished. Honestly I'm a bit frustrated as a similar approach used to work with NET Core 2.0.
I've uploaded my test project to GitHub:
https://github.com/wonea/MVC-API-Routing-Test
The test project details three middleware stages; SecurityMiddleware, UserValidatorMiddleware, WebSocketMiddleware. So upon booting the API you can set breakpoints on each of the individual stages and they will be hit in the correct order. However upon passing the HTTPContext on the final middleware stage it returns but does not hit the controller.
In Startup.cs I configure my services:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddApplicationInsightsTelemetry(_configuration);
// memory cache
services.AddMemoryCache();
services.AddMvc()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.IgnoreNullValues = true;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
// CORS
var corsBuilder = new CorsPolicyBuilder();
services.AddCors(builder =>
{
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.AllowAnyOrigin();
corsBuilder.WithOrigins("*");
corsBuilder.AllowCredentials();
});
}
I've detailed the request pipeline, setting up WebSockets, performing the middleware, and then triggeringmy controller as an endpoint.
public void Configure(IApplicationBuilder app, IHostApplicationLifetime appLifetime)
{
appLifetime.ApplicationStarted.Register(OnStarted);
appLifetime.ApplicationStopping.Register(OnStopping);
appLifetime.ApplicationStopped.Register(OnStopped);
Console.CancelKeyPress += (sender, eventArgs) =>
{
appLifetime.StopApplication();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
app.UseRouting();
app.UseSecurityMiddleware();
app.UseUserValidation();
// websockets
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(120)
};
app.UseWebSockets(webSocketOptions);
app.UseWebSocketMiddleware();
// put last so header configs like CORS or Cookies etc can fire
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I'm only ever going to need one controller, so don't need any fancy config.
I see your controller is setup like
[Route("api/[controller]")]
public class MainController : Controller
{
[HttpGet]
public string Get()
Note the docs say
Replace [controller] with the name of the controller, which by convention is the controller class name minus the "Controller" suffix.
For example, I have a "Work" controller that looks like
[Route("api/Work")]
[ApiController]
public class WorkController : ControllerBase
{
(it also has the ApiController attribute).
This would then be accessed at httpx://localhost/api/Work
You can then specify other endpoints on the api with the parameter to the HttpGet or HttpPost attribute.
[HttpGet("Test")]
public async Task<ActionResult<string>> Test()
{
Would be httpx://localhost/api/Work/Test
Figured it out, my routing was at fault.
My launch settings were point directly to the controllers location
Then my controller was duplicating the routing. So the main controller was being resolved on.
localhost:51234/api/main/api/main
I've flattened the main controller's routing now
I'm currently upgrading a project from ASP.NET WebAPI 5.2.6 (OWIN) to ASP.NET Core 2.1.1 (Kestrel).
Our project is a single page application and we communicate via WebAPI with the client. Therefore I wanted to annotate the controllers wit the new ApiController attribute.
Unfortunately it seems that the binding source parameter inference isn't working as expected (at least for me). I assumed based on the docs, that complex types (e.g. my LoginRequest) are inferred as [FromBody].
Code (Controller & Startup)
// AccountController.cs
[Route("/account"), ApiController]
public class AccountController : ControllerBase
{
[HttpPost("backendLogin"), AllowAnonymous]
public async Task<ActionResult<LoginResponse>> BackendLogin(LoginRequest lr)
{
await Task.CompletedTask.ConfigureAwait(false); // do some business logic
return Ok(new LoginResponse {UserId = "123"});
}
// Models
public class LoginRequest {
public string Email { get; set; }
public string Password { get; set; }
}
public class LoginResponse {
public string UserId { get; set; }
}
}
// Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvcCore()
.AddJsonFormatters(settings => {
settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.Configure<ApiBehaviorOptions>(options => {
// options.SuppressConsumesConstraintForFormFileParameters = true;
// options.SuppressInferBindingSourcesForParameters = true;
// options.SuppressModelStateInvalidFilter = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
Problem
Calling the controller from the client via Ajax call (Content-Type: application/x-www-form-urlencoded;) results in a 400 (bad request) response, with content {"":["The input was not valid."]}. On the server I get the following trace output:
Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor: Information: Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.SerializableError'.
If I change the options.SuppressInferBindingSourcesForParameters in ConfigureServices to true, it seems to work. This is strange, since this setting should disable the binding inference or have I misconceived something? Is this a bug in ASP.NET core or am I missing something?
Btw. it also works if I ommit the ApiController attribute, but I guess this is not the real solution to this problem.
Furthermore I would be happy if I don't need to change anything on the client side (adding headers, change content types, ...), because there are a lot of Ajax calls out there and I just want to upgrade the server side components.
I also asked this question on the official ASP.NET Core MVC repo.
One of the members (pranavkm) came back with an answer, which I will just quote here:
ApiController is designed for REST-client specific scenarios and isn't designed towards browser based (form-urlencoded) requests. FromBody assumes JSON \ XML request bodies and it'll attempt to serialize it, which is not what you want with form url encoded content. Using a vanilla (non-ApiController) would be the way to go here.
So for now I will omit the [ApiController] attribute.
In a later step I may change the client calls to use a JSON body, so I can readd the attribute.
I recently upgraded from NServiceBus 5x to 6.0.0-beta0004 to be able to host a ASP.NET Core application (whose main function is to listen to NServiceBus messages). I'm having problems with the startup of the host as the endpoint doesn't seem to subscribe to the publisher.
I am using the pubsub example to fix the problem. Apart from the projects that are in there by default, I added one extra project, a fresh ASP.NET Core project (based on the full .NET framework). I tried to use the exact same NServiceBus configuration, but instead of using the app/web.config, I am using the following configuration:
public class ConfigurationSource : IConfigurationSource
{
public T GetConfiguration<T>() where T : class, new()
{
UnicastBusConfig config = new UnicastBusConfig()
{
MessageEndpointMappings = new MessageEndpointMappingCollection()
};
var endpointMapping = new MessageEndpointMapping
{
AssemblyName = "Shared",
Endpoint = "Samples.PubSub.MyPublisher"
};
config.MessageEndpointMappings.Add(endpointMapping);
return config as T;
}
}
The Startup class was extended with the following code:
public IConfigurationRoot Configuration
{
get;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddSingleton(this.GetBus());
}
private IEndpointInstance GetBus()
{
LogManager.Use<DefaultFactory> ().Level(NServiceBus.Logging.LogLevel.Info);
var endpointConfiguration = new EndpointConfiguration("Samples.PubSub.Subscriber3");
endpointConfiguration.UseSerialization<JsonSerializer>();
endpointConfiguration.DisableFeature<AutoSubscribe>();
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();
// Skip web.config settings and use programmatic approach
endpointConfiguration.CustomConfigurationSource(new ConfigurationSource());
var endpointInstance = Endpoint.Start(endpointConfiguration).Result;
endpointInstance.Subscribe<IMyEvent>().Wait();
return endpointInstance;
}
The Message Handler is identical to the other Subscriber projects in the solution:
public class EventMessageHandler : IHandleMessages<IMyEvent>
{
static ILog log = LogManager.GetLogger<EventMessageHandler>();
public Task Handle(IMyEvent message, IMessageHandlerContext context)
{
log.Info($"Subscriber 2 received IEvent with Id {message.EventId}.");
log.Info($"Message time: {message.Time}.");
log.Info($"Message duration: {message.Duration}.");
return Task.FromResult(0);
}
}
If I run the sample, I notice Subscriber1 and Subscriber2 are subscribed perfectly and they receive messages if I execute some of the commands in the Publisher console application. However Subscriber3 doesn't appear to be doing anything. No exceptions are thrown and in this sample I don't seem to find any relevant log information that could lead to misconfiguration.
Has anyone tried a similar setup with ASP.NET Core & NServiceBus 6 and if so, what should I do next?
Update 04/04/2017
There are some updates that provide some interesting insights:
https://groups.google.com/forum/#!topic/particularsoftware/AVrA1E-VHtk
https://particular.net/blog/nservicebus-on-net-core-why-not
I've created a basic webAPI project (blank web project with webAPI checked) and added the owin nuget packages to the project.
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Host.SystemWeb
Owin
I've then created a Logging class, and hooked it up via startup
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
Debug.WriteLine("Startup Called");
var config = new HttpConfiguration();
WebApiConfig.Register(config);
appBuilder.UseWebApi(config);
appBuilder.Use(typeof(LoggingMiddleware));
}
}
public class LoggingMiddleware
{
private AppFunc Next { get; set; }
public LoggingMiddleware(AppFunc next)
{
Next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
Debug.WriteLine("Begin Request");
await Next.Invoke(environment);
Debug.WriteLine("End Request");
}
}
When I run the project, and the default page opens, I see the Begin/End requests called (twice, as it happens, not sure why that is).
However, if I try to call an /api route (such as `/api/ping/'), the request completes successfully, but I do not see the Begin/End request states in the log.
What am I missing with this?
Owin executes the middleware items in the order that they are registered, ending at the call to the controller (appBuilder.UseWebApi(config)) which does not appear to call next.Invoke(). Given that the code in the question has the Logging Middleware class registered after the UseWebApi call, this causes it to never be called for API requests.
Changing the code to:
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
//.....
//This must be registered first
appBuilder.Use(typeof(LoggingMiddleware));
//Register this last
appBuilder.UseWebApi(config);
}
}
resolves the issue.
I'm finding my feet with Web Api 2, Owin and Autofac and need some guidance, please.
Overview
I have an Owin self-hosted Web Api that uses Autofac for IoC and dependency injection. The project is a console app acting like a service, meaning it can be stopped and started. I have an Authentication controller with two constructors: one parameter-less and the other injects a repository.
Problem
When I run the service and call the api, my parameter-less constructor is called and my repository never gets injected (_repository = null).
Research
I've done a fair bit of research and found some helpful projects on Github, which I replicated to the tee but I'm missing a big part of the puzzle. This was helpful but didn't solve my problem. I read this question on Stack Overflow and Dane Sparza had a nice demo project but I couldn't find a clear solution. The problem is not the self-hosting but the dependency injection.
My code (thinned out for explanation)
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
var connectioninfo = ConnectionInfo.FromAppConfig("mongodb");
var builder = new ContainerBuilder(); // Create the container builder.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // Register the Web API controllers.
builder.Register(c => new Logger()).As<ILogger>().InstancePerRequest(); // Register a logger service to be used by the controller and middleware.
builder.RegisterType<AuthenticationRepository>().As<IAuthenticationRepository>().WithParameter(new NamedParameter("connectionInfo", connectioninfo)).InstancePerRequest();
var container = builder.Build();
var resolver = new AutofacWebApiDependencyResolver(container); // Create an assign a dependency resolver for Web API to use.
GlobalConfiguration.Configuration.DependencyResolver = resolver; // Configure Web API with the dependency resolver
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
app.UseAutofacWebApi(config); // Make sure the Autofac lifetime scope is passed to Web API.
}
Program.cs
static void Main(string[] args)
{
var service = new ApiService(typeof(Program), args);
var baseAddress = "http://localhost:9000/";
IDisposable _server = null;
service.Run(
delegate()
{
_server = WebApp.Start<Startup>(url: baseAddress);
},
delegate()
{
if (_server != null)
{
_server.Dispose();
}
}
);
}
ApiController
public class AuthenticationController : ApiController
{
private IAuthenticationRepository _repository;
public AuthenticationController() { }
public AuthenticationController(IAuthenticationRepository repository)
{
_repository = repository;
}
[AllowAnonymous]
public IHttpActionResult Authenticate(string name, string password)
{
if (_repository == null)
return BadRequest("User repository is null.");
var valid = _repository.AuthenticateUser(name, password);
return Ok(valid);
}
}
You should be using the HttpConfiguration with which you're bootstrapping OWIN everywhere.
So, this:
GlobalConfiguration.Configuration.DependencyResolver = resolver;
Should become:
config.DependencyResolver = resolver;
Other than that, everything looks good. Api controllers are registered, although you're not giving them a scope. Not sure if in Autofac scoping defaults to per-request for controllers or if it has the notion of per-request scoping at all (I know that LightInject has it).
Looking around, I think you followed the example on the Google Code repo for Autofac, which indeed uses GlobalConfiguration. Instead, if you look at the GitHub example, it is a bit different. Try to make the changes according to this. Including this:
// This should be the first middleware added to the IAppBuilder.
app.UseAutofacMiddleware(container);
2016 update
What I said above still applies, but something extra from Autofac's docs (thanks Brad):
A common error in OWIN integration is use of the
GlobalConfiguration.Configuration. In OWIN you create the
configuration from scratch. You should not reference
GlobalConfiguration.Configuration anywhere when using the OWIN
integration.