How to set custom URL in MVC4 - asp.net-mvc-4

I need in MVC4 in C#...
{id}.example.com or
{id}.example.com/{controller}/{action}
Or
In localhost how can I test it.
I means, can I debug this code in below format ...
{id}.localhost:51782 or
{id}.localhost:51782/{controller}/{action}
Please explain this in full steps.

You need to handle your requests with "virtual" URL, for example {user}.example.com and internally update httpcontext path using httpContext.RewritePath(). Finally you will have two types of URIs.
Virtual: {id}.example.com/{controller}/{action}
Real: {id}/{controller}/{action}
Handling is possible in Application_BeginRequest inside your Global.asax.cs file
protected void Application_BeginRequest(object sender, EventArgs e)
{
var domainHandler = new DomainsHandler(Context);
domainHandler.Handle();
}
And example of DomainHandler:
public class DomainsHandler
{
private readonly HttpContext httpContext;
public DomainsHandler(HttpContext httpContext)
{
this.httpContext = httpContext;
}
public void Handle()
{
httpContext.RewritePath(path);
}
}
Rewriting logic inside Handle() method is up to you.

Related

Session.IsNewSession in ASP.NET Core

I am migrating an ASP.NET MVC application to ASP.NET Core 3.1.
And I have a code to check if the session was timed out in my controller, like this:
if (Session.IsNewSession) {
How can I check it in ASP.NET Core?
Thanks
The default implementation of ISession is DistributedSession. This does not expose any property for IsNewSession although its constructor accepts a parameter named isNewSessionKey. So you can use reflection to get that private field of _isNewSessionKey to check it. But that way is not very standard, the name may be changed in future without notifying you any design-time error.
You have several points to intercept and get the info here. The first point is to create a custom ISessionStore (default by DistributedSessionStore) to intercept the call to ISessionStore.Create which gives access to isNewSessionKey. You can capture that value into a request feature just like how the framework set the ISessionFeature after creating the session. Here's the code:
//create the feature interface & class
public interface ISessionExFeature {
bool IsNewSession { get; }
}
public class SessionExFeature : ISessionExFeature {
public SessionExFeature(bool isNewSession){
IsNewSession = isNewSession;
}
public bool IsNewSession { get; }
}
//the custom ISessionStore
public class CustomDistributedSessionStore : DistributedSessionStore, ISessionStore
{
readonly IHttpContextAccessor _httpContextAccessor;
public CustomDistributedSessionStore(IDistributedCache cache,
ILoggerFactory loggerFactory,
IHttpContextAccessor httpContextAccessor) : base(cache, loggerFactory)
{
_httpContextAccessor = httpContextAccessor;
}
ISession ISessionStore.Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
{
var httpContext = _httpContextAccessor.HttpContext;
if(httpContext != null)
{
var sessionExFeature = new SessionExFeature(isNewSessionKey);
httpContext.Features.Set<ISessionExFeature>(sessionExFeature);
}
return Create(sessionKey, idleTimeout, ioTimeout, tryEstablishSession, isNewSessionKey);
}
}
//register the custom ISessionStore inside Startup.ConfigureServices
services.Replace(new ServiceDescriptor(typeof(ISessionStore), typeof(CustomDistributedSessionStore), ServiceLifetime.Transient));
//an extension method to help get the ISessionExFeature conveniently
public static class SessionExFeatureHttpContextExtensions {
public static bool HasNewSession(this HttpContext context){
return context.Features.Get<ISessionExFeature>()?.IsNewSession ?? false;
}
}
To use it in your code:
if (HttpContext.HasNewSession()) {
//...
}
Another point to intercept and get the info is customize both the ISessionStore and ISession. Which means you create a sub class of DistributedSession and expose the property for IsNewSession. That may require more code but it looks more like the old way of getting the info (directly from the Session not kind of via an extension method on HttpContext).

IHttpClientFactory using in ActionFilterAttribute [duplicate]

I am trying to inject a service into my action filter but I am not getting the required service injected in the constructor. Here is what I have:
public class EnsureUserLoggedIn : ActionFilterAttribute
{
private readonly ISessionService _sessionService;
public EnsureUserLoggedIn()
{
// I was unable able to remove the default ctor
// because of compilation error while using the
// attribute in my controller
}
public EnsureUserLoggedIn(ISessionService sessionService)
{
_sessionService = sessionService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Problem: _sessionService is null here
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
}
And I am decorating my controller like so:
[Route("api/issues"), EnsureUserLoggedIn]
public class IssueController : Controller
{
}
Startup.cs
services.AddScoped<ISessionService, SessionService>();
Using these articles as reference:
ASP.NET Core Action Filters
Action filters, service filters and type filters in ASP.NET 5 and MVC 6
Using the filter as a ServiceFilter
Because the filter will be used as a ServiceType, it needs to be registered with the framework IoC. If the action filters were used directly, this would not be required.
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();
...
}
Custom filters are added to the MVC controller method and the controller class using the ServiceFilter attribute like so:
[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}
There were other examples of
Using the filter as a global filter
Using the filter with base controllers
Using the filter with an order
Take a look, give them a try and see if that resolves your issue.
Hope this helps.
Global filters
You need to implement IFilterFactory:
public class AuthorizationFilterFactory : IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// manually find and inject necessary dependencies.
var context = (IMyContext)serviceProvider.GetService(typeof(IMyContext));
return new AuthorizationFilter(context);
}
}
In Startup class instead of registering an actual filter you register your filter factory:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilterFactory());
});
One more way for resolving this problem. You can get your service via Context as in the following code:
public override void OnActionExecuting(ActionExecutingContext context)
{
_sessionService = context.HttpContext.RequestServices.GetService<ISessionService>();
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
Please note that you have to register this service in Startup.cs
services.AddTransient<ISessionService, SessionService>();
Example
private ILoginService _loginService;
public override void OnActionExecuting(ActionExecutingContext context)
{
_loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
}
Hope it helps.
After reading this article ASP.NET Core - Real-World ASP.NET Core MVC Filters (Aug 2016) I implemented it like this:
In Starup.cs / ConfigureServices:
services.AddScoped<MyService>();
In MyFilterAttribute.cs:
public class MyFilterAttribute : TypeFilterAttribute
{
public MyFilterAttribute() : base(typeof (MyFilterAttributeImpl))
{
}
private class MyFilterAttributeImpl : IActionFilter
{
private readonly MyService _sv;
public MyFilterAttributeImpl(MyService sv)
{
_sv = sv;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_sv.MyServiceMethod1();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_sv.MyServiceMethod2();
}
}
}
In MyFooController.cs :
[MyFilter]
public IActionResult MyAction()
{
}
Edit: Passing arguments like [MyFilter("Something")] can be done using the Arguments property of the TypeFilterAttribute class: How do I add a parameter to an action filter in asp.net? (rboe's code also shows how to inject things (the same way))
While the question implicitly refers to "filters via attributes", it is still worth highlighting that adding filters "globally by type" supports DI out-of-the-box:
[For global filters added by type] any constructor dependencies will be populated by dependency injection (DI). Adding a filter by type is equivalent to filters.Add(new TypeFilterAttribute(typeof(MyFilter))).
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
With regards to attribute-based filters:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they're applied. This is a limitation of how attributes work.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
However, as mentioned in the previous answers to the OP, there are ways of indirection that can be used to achieve DI. For the sake of completeness, here are the links to the official docs:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory implemented on your attribute

Set up logging with Blazor WebAssembly

I'm doing some experiments with Blazor and want to set up logging. I see that Blazor logs to Microsoft.Extensions.Logging out of the box and that the log messages go to the developer console inside the browser. That is a nice start.
Now I want to try and log messages to other destinations as well. It could be a cloud-service. I'm wondering where to set that up. In ASP.NET Core, you would set it up using the ConfigureLogging method in Program.cs. But this isn't available with Blazor:
public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
BlazorWebAssemblyHost.CreateDefaultBuilder()
.UseBlazorStartup<Startup>()
.ConfigureLogging(...); // <- compile error
As a fallback, I'm trying to set it up through ConfigureServices in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(builder => builder
.AddMyLogger()
.SetMinimumLevel(LogLevel.Information));
}
with AddMyLogger:
public static ILoggingBuilder AddMyLogger(this ILoggingBuilder builder)
{
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
return builder;
}
and MyLoggerProvider:
public class MyLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new MyLogger();
}
public void Dispose()
{
}
}
and MyLogger:
public class MyLogger : ILogger
{
public MyLogger()
{
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
}
}
The AddMyLogger-method is called but my logger is never created or receives any Log-calls.
Am I doing something wrong here or is logging with Blazor WebAssembly simply not ready yet?
I was trying something similar. In my case, the Log method in MyLogger gets called; however it fails at following line of code
using (var streamWriter = new StreamWriter(fullFilePath, true)) //Fails here
{
streamWriter.WriteLine(logRecord);
}
When I put it in try catch block, I got the exception "Children could not be evaluated".
While researching I came across following link. Steve Sanderson's response might make sense of the behavior
Reading local files #16156
BTW It's been a long time, please let me know the solution you came up with.

HttpContext.Current.Session is null when injecting it to interceptor / or using in inside interceptor (mvc4 webapi)

I have mvc4 WebAPI application with castle Windsor interception.
The interceptor class needs HttpContext.Current.Session.
When I call it directly from the interceptor, it is null.
So I read here that I need to inject the Session and not just access it in the interceptor.
This the code I ended up with...
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container.Kernel));
this.RegisterDependencyResolver();
this.container.Install(new WindsorWebApiInstaller());
}
public class WindsorWebApiInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// the following interceptor needs Session object
container.Register( Component.For<IInterceptor>()
.ImplementedBy<SecurityInterceptor>()
.LifestylePerWebRequest()
.Named("SecurityInterceptor"));
// session is null here, So Castle wont inject it and throw exception...
container.Register(
Component.For<HttpSessionStateBase>().UsingFactoryMethod(
() => new HttpSessionStateWrapper(HttpContext.Current.Session)).LifestylePerWebRequest());
}
}
Is there any other way to access the session from the interceptor?
Thanks
I always forget that WEBAPI is not MVC.
this is not castle issue.
this did the trick!
public override void Init()
{
this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
System.Web.HttpContext.Current.SetSessionStateBehavior(
SessionStateBehavior.Required);
}
https://stackoverflow.com/a/15038669/936651
+1 to #Soren

In WCF how do I remove the 404 response body?

I have a WCF service configured and I'm using routing to configure it. Everything is working the way I want it, except the 404 messages have a body stating Service Endpoint not found.
I'd like the 404 to have an empty response body.
Here is my route registration:
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
routes.Add(new ServiceRoute("RootService", new WebServiceHostFactory(), typeof(ServiceProvider)));
}
Here is my service class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceContract]
public class ServiceProvider
{
[WebGet]
public Test ValidUrl()
{
return new Test();
}
}
How do I make the response for this url http://localhost/RootService have an empty 404 body?
I found a few ways to do this and I've listed two below. They key is having the UriTemplate set as *. This makes the method match all routes that aren't explicitly matched otherwise.
[WebGet(UriTemplate="*")]
public void ErrorForGet()
{
throw new WebFaultException(HttpStatusCode.NotFound);
}
I don't like this way as well, but it works:
[WebGet(UriTemplate="*")]
public void ErrorForGet()
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
}
Both of these methods have overloads that take a string as a message to provide to the requesting client. The WebFaultException needs to be like this going that route though: throw new WebFaultException<string>("Resource not found", HttpStatusCode.NotFound);