ServiceStack default Razor view with service - servicestack

I want to host a very simple razor page inside a self host SS app.
I need the / path to resolve to the default.cshtml - this works out of the box.
But i need to access the user auth session inside the view. To do this I am guessing I need a service to create the model for default.cshtml
Everything I have tried so far doesn't work and I can't create a DefaultRequest with route / as that isn't allowed.
Anyone got any clues as to what I need to do?
I have tried with fall back route but no luck:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
public class DefaultService : Service
{
public DefaultService ()
{
}
public object Get(Fallback request){
return new HttpResult() // #6
{
View = "Rockstars" // #1
};
}
}

Your typed UserAuth session is directly accessible in your Razor Views base ViewPageBase with base.SessionAs, e.g:
#{
var session = base.SessionAs<CustomUserSession>();
}
You've also got access to your dynamic session bag with base.SessionBag as well as base.IsAuthenticated to determine if the user is authenticated or not.
Fallback Route
In order to invoke a Service to handle your default page you need to use a Fallback Route, e.g:
[FallbackRoute("/{Path*}")]
public class DefaultPage
{
public string Path { get; set; }
}
A Fallback Service can be used to handle every unmatched request including the root / page.

Related

SwaggerRequestExample attribute does not work in ASP.NET MVC 5 (.NET Framework 4.5.2)

I am toying with Swashbuckle.Examples package (3.10.0) in an ASP.NET MVC project. However, I cannot make request examples appear within the UI.
Configuration (SwaggerConfig.cs)
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c => {
c.SingleApiVersion("v1", "TestApp.Web");
c.IncludeXmlComments(string.Format(#"{0}\bin\TestApp.Web.xml", System.AppDomain.CurrentDomain.BaseDirectory));
c.OperationFilter<ExamplesOperationFilter>();
c.OperationFilter<DescriptionOperationFilter>();
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
})
.EnableSwaggerUi(c => { });
}
Request example classes
public class EchoRequestExample : IExamplesProvider
{
public object GetExamples()
{
return new EchoInput { Value = 7 } ;
}
}
public class EchoInput
{
public int Value { get; set; }
}
Action
[HttpGet]
[Route("Echo")]
[CustomApiAuthorize]
[SwaggerRequestExample(typeof(EchoInput), typeof(EchoRequestExample))]
[ResponseType(typeof(EchoServiceModel))]
public HttpResponseMessage Echo([FromUri] EchoInput model)
{
var ret = new EchoServiceModel
{
Username = RequestContext.Principal.Identity.Name,
Value = value
};
return Request.CreateResponse(HttpStatusCode.OK, ret);
}
Swagger UI shows xml comments and output metadata (model and an example containing default values), but shows no request example. I attached to process and EchoRequestExample.GetExamples is not hit.
Question: How to make SwaggerRequestExample attribute work in ASP.NET MVC 5?
Note: Windows identity is used for authorization.
I received an answer from library owner here:
Swagger request examples can only set on [HttpPost] actions
It is not clear if this is a design choice or just a limitation, as I find [HttpGet] examples also relevant.
I know the feeling, lot's of overhead just for an example, I struggle with this for a while, so I created my own fork of swashbuckle, and after unsuccessful attempts to merge my ideas I ended up detaching and renaming my project and pushed to nuget, here it is: Swagger-Net
An example like that will be:
[SwaggerExample("id", "123456")]
public IHttpActionResult GetById(int id)
{
Here the full code for that: Swagger_Test/Controllers/IHttpActionResultController.cs#L26
Wondering how that looks like on the Swagger-UI, here it is:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=IHttpActionResult#/IHttpActionResult/IHttpActionResult_GetById

ASP.Net MVC5: How to redirect and show right error message when user does not have a specific role for action

after login user can go to any action but think when action is decorated with authorized attribute and role names are specific there. just refer a sample code.
public class HomeController : Controller
{
[Authorize(Roles = "Admin, HrAdmin")]
public ActionResult PayRoll()
{
return View();
}
}
suppose user Foo has no role like Admin or HRAdmin then what will happen when user foo will try to access PayRoll action ?
in this kind of situation i want to redirect user to my error page where i will show a friendly message to user. please guide me how to do it ?
do i need to write a custom authorized attribute from there i need to check user has those roles are not and then redirect user from there?
I don't know if that's the best way to do it, but here's how I did it:
using System.Web.Mvc;
namespace YourNamespace
{
public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
// Redirect to the login page if necessary
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(System.Web.Security.FormsAuthentication.LoginUrl + "?returnUrl=" + filterContext.HttpContext.Request.Url);
return;
}
// Redirect to your "access denied" view here
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectResult("~/Account/Denied");
}
}
}
}
Controller:
public class HomeController : Controller
{
[AccessDeniedAuthorize(Roles = "Admin, HrAdmin")]
public ActionResult PayRoll()
{
return View();
}
}
That's all you have to do if your User has its Roles defined correctly. If you are not using ASP.NET Identity to manage your users and roles, you will need some more code to make this work, in that case this might help you: How can I attach a custom membership provider in my ASP.NET MVC application?.

Current URL from PreApplicationStart

I am using StructureMap.MVC5 which relies on the PreApplicationStart method to register an HttpModule and initialize the IoC container. Is it possible to get the server name the page is executing on at this point so that I can set an environment specific property in the IoC initialization?
I was able to resolve this issue by moving the IoC initialization to the Application_BeginRequest method, where the HttpContext has already been set. In order to ensure that the IoC container was not reinitialized on each call to Application_BeginRequest, I was able to use a mutex block, thus negating the need to move this code to somewhere earlier in the page lifecycle.
public class FirstInitialization
{
private static Object s_lock = new Object();
public static string URL { get; protected set; }
// Initialise only on the first request
public static string Initialize(HttpContext context)
{
if (string.IsNullOrEmpty(URL))
{
lock (s_lock)
{
if (string.IsNullOrEmpty(URL))
{
URL = HttpContext.Current.Request.Url.AbsoluteUri;
}
DependencyResolver.SetResolver(IoC.GetDependencyResolver(URL));
}
}
return URL;
}
}

Have Swagger to substitute servicestack meta

I was wondering if it's possible to have swagger to serve pages at place of SS metadata page... I'm asking this since SS metadata is quite usefull when you've a lot of services
as far I've seen I can remove the feature on SS configuration, disable the httphandler but don't know how to go further
Thanks
So remove the MetaDataFeature in the AppHost Configure method:
SetConfig(new HostConfig {
EnableFeatures = Feature.All.Remove(Feature.Metadata)
});
Then create this simple MetaData service, that redirects to Swagger.
[Route("/metadata/{cmds*}", "GET")]
public class RedirectToSwaggerRequest : IReturnVoid
{
public string cmds { get; set; }
}
[Restrict(VisibleLocalhostOnly = true)]
public class MetadataService : Service
{
public void Get(RedirectToSwaggerRequest request)
{
base.Response.Redirect("/swagger-ui");
}
}
Note: {cmds*} in the route above will catch requests for /metadata, /metadata/something & /metadata/somethingelse etc.
Then when a request goes to /metadata then it will redirect to Swagger instead.

Is it possible to inject an instance of object to service at runtime

I have created a plugin which inspects a param in the query string and loads up a user object based on this ID and populates
any request DTO with it. (All my request DTO's inherit from BaseRequest which has a CurrentUser property)
public class CurrentUserPlugin : IPlugin
{
public IAppHost CurrentAppHost { get; set; }
public void Register(IAppHost appHost)
{
CurrentAppHost = appHost;
appHost.RequestFilters.Add(ProcessRequest);
}
public void ProcessRequest(IHttpRequest request, IHttpResponse response, object obj)
{
var requestDto = obj as BaseRequest;
if (requestDto == null) return;
if (request.QueryString["userid"] == null)
{
throw new ArgumentNullException("No userid provided");
}
var dataContext = CurrentAppHost.TryResolve<IDataContext>();
requestDto.CurrentUser = dataContext.FindOne<User>(ObjectId.Parse(requestDto.uid));
if (requestDto.CurrentUser == null)
{
throw new ArgumentNullException(string.Format("User [userid:{0}] not found", requestDto.uid));
}
}
}
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there. Is there a way to make data from plugins globally available to my services? I am also wondering if there is another way of instantiating this object as for my unit tests, the Plugin is not run - as I call my service directly.
So, my question is, instead of using Plugins can I inject a user instance to my services at run time? I am already using IoC to inject different Data base handlers depending on running in test mode or not but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
Below is an example of how I inject my DataContext in appHost.
container.Register(x => new MongoContext(x.Resolve<MongoDatabase>()));
container.RegisterAutoWiredAs<MongoContext, IDataContext>();
Here is an example of my BaseService. Ideally I would like to have a CurrentUser property on my service also.
public class BaseService : Service
{
public BaseService(IDataContext dataContext, User user)
{
DataContext = dataContext;
CurrentUser = user; // How can this be injected at runtime?
}
public IDataContext DataContext { get; private set; }
public User CurrentUser { get; set; }
}
Have you thought about trying to use the IHttpRequest Items Dictionary to store objects. You can access these Items from any filter or service or anywhere you can access IHttpRequest. See the src for IHttpRequest.
Just be mindful of the order that your attributes, services and plugins execute and when you store the item in the Items dictionary.
Adding:
We don't want to use HttpContext inside of the Service because we want use Service in our tests directly.
Advantages for living without it
If you don't need to access the HTTP
Request context there is nothing stopping you from having your same
IService implementation processing requests from a message queue which
we've done for internal projects (which incidentally is the motivation
behind the asynconeway endpoint, to signal requests that are safe for
deferred execution).
http://www.servicestack.net/docs/framework/accessing-ihttprequest
And we don't use http calls to run tests.
So our solution is:
public class UserService
{
private readonly IDataContext _dataContext;
public UserService(IDataContext dataContext)
{
_dataContext = dataContext;
}
public User GetUser()
{
var uid = HttpContext.Current.Request.QueryString["userId"];
return _dataContext.Get<User>(uid);
}
}
and
container.Register(x => new UserService(x.Resolve<IDataContext>()).GetUser()).ReusedWithin(ReuseScope.Request);
This is service signature:
public SomeService(IDataContext dataContext, User user) { }
Any suggestions?
I need to have this User object available in my services but I don't want to inspect the DTO every time and extract from there
How will your application know about the user if you're not passing the 'userid' in the querystring? Could you store the user data in the Session? Using a Session assumes the client is connected to your app and persists a Session Id (ss-id or ss-pid cookie in ServiceStack) in the client that can be looked up on the Server to get the 'session data'. If you can use the Session you can retrieve the data from your service doing something like
base.Session["UserData"] or base.SessionAs<User>();
Note: you will need to save your User data to the Session
Is there a way to make data from plugins globally available to my services? but I can't see how to achieve this for User object which would need to be instantiated at the beginning of each request.
This sounds like you want a global request filter. You're kind of already doing this but you're wrapping it into a Plugin.

Resources