Independent sessions for each tabs C# WebApi / Angular2+ - c#

We are building aWeb app that allows users to open different projects they created using this app. The front-end is an Angular 4 app with a REST Architecture and the backend a C# Asp.net Web Api.
Our problem is that we store all our backend datas in a Session (because it's a huge object with mathematical datas and formulas in it), and when we open multiple projects in different tabs, the session become in common between each tabs so it causes interferences.
We are trying to find a way to attach each instance of the app (each project) with an unique session in the server. We look for using GUID stored in window.name for each tab, then sent to the server which has HttpContext.Current.Session["GUIDSessionID"]; that stores our object but we did not find the way to make it work, as we are beginners in c# development.
Here are the links we look for to find a solution (without success) :
http://blog.gauffin.org/2012/02/get-a-unique-session-in-each-browser-tab/
https://www.codeproject.com/Questions/364203/Stop-Sharing-Session-State-between-Multiple-Tabs-o
Thanks in advance.
Nikolas

Generate a unique session id a hash or something.
In Angular to store it can put locale localStorage.setItem('GUIDSessionID', 'YourSessionID');.
If you want to get it even after a refresh localStorage.getItem('GUIDSessionID');
If you login with a new generate new SessionID remove old localStorage.removeItem('GUIDSessionID') and set new.

The easiest way to achieve this is by -
reading the project ID from URL/querystring and put that in some hidden field on the page.
Create all the session variables with key starting that project ID.
For example, if your project ID is '654654481CEG', your session assignment will be something like this:
Session[string.Format("{0}-projectName", "654654481CEG")] = "WhateverYourValueIs";
While reading the value back from session, you will do the same -
string strprojectName = Session[string.Format("{0}-projectName", "654654481CEG")].ToString();

I had the same problem with a coworker and we used sort of sub-sessions:
//We changed this
HttpContext.Current.Session["x"] = x;
HttpContext.Current.Session["y"] = y;
//To this
HttpContext.Current.Session["guid"] = new SessionContent{x = x, y = y };
and we did as follow :
BackEnd
public static class SessionUtils
{
public class SessionContent
{
public XClass xProperty{ get; set; }
public YClass yProperty{ get; set; }
}
public static string GetSessionGUID(IHttpRouteData route)
{
return route.Values["guid"].ToString();
}
public static XClass GetSessionXProperty(HttpContextBase httpContextBase, IHttpRouteData route)
{
return ((SessionUtils.SessionContent)httpContextBase.Session[GetSessionGUID(route)]).xProperty;
}
public static void SetSessionXProperty(HttpContextBase httpContextBase, IHttpRouteData route, XClass xProperty)
{
((SessionUtils.SessionContent)httpContextBase.Session[GetSessionGUID(route)]).xProperty= xProperty;
}
public static YClass GetSessionYProperty(HttpContextBase httpContextBase, IHttpRouteData route)
{
return ((SessionUtils.SessionContent)httpContextBase.Session[GetSessionGUID(route)]).yProperty;
}
public static void SetSessionYProperty(HttpContextBase httpContextBase, IHttpRouteData route, YClass yProperty)
{
((SessionUtils.SessionContent)httpContextBase.Session[GetSessionGUID(route)]).yProperty= yProperty;
}
}
Here is an controller for example:
public class TestController : ApiController
{
private HttpContextBase _httpContext;
public TestController ()
{
_httpContext = new HttpContextWrapper(HttpContext.Current);
}
public AuditTrailController(HttpContextBase context)
{
_httpContext = context;
}
[HttpGet]
[Route("Send}")]
public HttpResponseMessage Send()
{
XClass x = SessionUtils.GetSessionXProperty(_httpContext, Request.GetRouteData());
HttpResponseMessage response = new HttpResponseMessage();
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(JsonConvert.SerializeObject(x), System.Text.Encoding.UTF8, "application/json");
return response;
}
}
And in Angular front we just generate a GUID, send it to the back end while initiating the project and add it to each request we make.
window.name = UUID.UUID();
Then in services :
return this.http.get(environment.baseUrl + '/' + window.name + '/myController/')
.map(res => res.json());

Related

Injecting HttpContext-derived values into some classes

Here is the problem
A multi-layered approach. The end point/action used BLL Provider which must trim data not only using certain business criteria but also security criteria. For example, a super user can view all items and plain user can only view items assigned to his group.
The knee-jerk reaction to this - public IEnumerable<Item> GetItems(int? userId, string color, string location), where if user Id is not provided - get all items. But what if I want to put additional layer of protection - derive query based on a special MyIdentityProvider.
public class BllItemDataProvider : IBllItemDataProvider
{
private IMyIdentityProvider _myIdentity;
public BllItemDataProvider(IMyIdentityProvider myIdentity)
{
_myIdentity = myIdentity;
}
public BllItemDataProvider(IMyIdentityProvider myIdentity)
{
_myIdentity = myIdentity;
}
}
MyIdentityProvider would have userId, isSuperUser, other flags, etc.
The question is, how to wire it up so that in Web API IHttpContextAccessor or the HttpContext will hydrate MyIdentityProvider from the Claims I find in the [controller].HttpContext.User.Claims. So my controller would look like
public class ItemController : ControllerBase
{
private IBllItemDataProvider _prov
public ItemController (IBllItemDataProvider prov)
{
_prov = prov;
}
[HttpGet("[action]/{color}/{location?}")]
public IActionResult SomeItems (string color, string location)
{
_prov = prov.GetItems(color, location);
}
}
Or, I should just create a base controller which can obtain that and inherit from it?
public abstract class ClaimControllerBase: ControllerBase - in constructor parse claims and setup thread principal, which can then be accessed down in the pipeline?
Thanks
Additionally I can add that these flags in the MyIdentityProvider will be taken from HttpContext.User.Claims which in turn come from JWT token and filled by Identity Framework. So, literally, I can take those claims and slap it in the base controller into a thread. But I don't think this a pretty solution.
Alright, looks like this one goes into DIY, or solve it yourself
Added this code
// CREATED Helper
public class MyIdentityProviderHelper
{
public static IMyIdentityProvider FetchProvider(IHttpContextAccessor accessor)
{
var prov = new MyIdentityProvider();
// -- HERE parse claims and fill properties
return prov;
}
}
// ADDED to Startup
services.AddHttpContextAccessor();
services.AddTransient<IMyIdentityProvider>((s) => // factory method is the real secret here
{
return MyIdentityProviderHelper.FetchProvider(
s.GetService<IHttpContextAccessor>());
});
services.AddTransient<IItemProvider, RealItemProvider>();
// DECLARED RealItemProvider
public class RealItemProvider : IItemProvider
{
private IMyIdentityProvider _identity
public RealItemProvider (IMyIdentityProvider identity)
{
_identity = identity;
}
public IEnumerable<Item> GetItems(string color, string location)
{
IEnumerable<Item> items = null;
if (_identity.Roles.Contains(Roles.SysAdmin))
items = GetAllItems(color, location); // private
else
items = GetUserItems(color, location); // private
return items;
}
}
As you see, the main soul saver here is ability to provide a factory for the object and use factory of another object to pass the retrieved object

Azure Function, returning status code + JSON, without defining return in every part of logic

I have an Azure Function 2.x that reside on a static class that looks like this
[FunctionName("Register")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req, ILogger log)
{
MyTypeClass defReturn = new MyTypeClass();
HttpStatusCode defCode = HttpStatusCode.BadRequest;
/*
* Logics that might or might not changes
* defReturn and defCode value
*/
return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn))
}
How can i achieve the return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn)) part ? is there any such method or equivalent in Azure Functions 2.x ?
in Azure Functions 1.x i can do the equivalent with req.CreateResponse(defCode, defReturn) where req is HttpRequestMessage , but i'm trying to stick with 2.x template/standard
Additional explanation : The said Code should return HTTP 400 Bad Request with the defReturn as it's response body to the client. But when i change the defCode to HttpStatusCode.Accepted, it should return HTTP 202 Accepted with the same response body. How can i achieve this ?
Additional explanation#2 : (If i remember correctly) in ASP.NET Core 1.x i can exactly do like that, returning IActionResult by calling a static method StatusCode not StatusCodes (which is a static class that contains HTTP codes constants
Thank you
Quite late reply, but I was stumbling into the same problem today, so maybe this is helpful for other searchers
Option 1: Default Codes
This is stated in detail on the blog Here
Some codes like 200 and 400 are predefined and can be used by
return new OkObjectResult("Your message"); // 200
return new BadRequestObjectResult("Your error message"); // 400
These functions are not available for every known Status Codes but some of the most frequent.
Option 2: Manual setting Code
If you need specific codes, that are not provided by default, you can use the base classes and create them yourself.
To achieve the Teapot Response for example, you can just use
using Microsoft.AspNetCore.Http;
var result = new ObjectResult("Your message");
result.StatusCode = StatusCodes.Status418ImATeapot;
return result;
In this example, the Statuscode is used from the StatusCodes class, but you can use enter other codes as well (usually, just stick to these codes)
Also, the ObjectResult class offers additional formatting options, if needed.
You can create a model class in which you can define two properties, i.e. one form your status code and one for you Json object and later on you can return the complete model. Code sample would be like below:
public static class QueueTriggerTableOutput
{
[FunctionName("QueueTriggerTableOutput")]
[return: Table("outTable", Connection = "MY_TABLE_STORAGE_ACCT_APP_SETTING")]
public static Person Run(
[QueueTrigger("myqueue-items", Connection = "MY_STORAGE_ACCT_APP_SETTING")]JObject order,
ILogger log)
{
return new Person() {
PartitionKey = "Orders",
RowKey = Guid.NewGuid().ToString(),
Name = order["Name"].ToString(),
MobileNumber = order["MobileNumber"].ToString() };
}
}
public class Person
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Name { get; set; }
public string MobileNumber { get; set; }
}
on the receiving front, you can catch both the property.
P.S.- you have to change the return type of your function.
Hope it helps.

Best way to use HTTPClient in ASP.Net Core as a DI Singleton

I am trying to figure out how to best use the HttpClient class in ASP.Net Core.
According to the documentation and several articles, the class is best instantiated once for the lifetime of the application and shared for multiple requests. Unfortunately, I could not find an example of how to correctly do this in Core so I’ve come up with the following solution.
My particular needs require the use of 2 different endpoints (I have an APIServer for business logic and an API driven ImageServer), so my thinking is to have 2 HttpClient singletons that I can use in the application.
I’ve configured my servicepoints in the appsettings.json as follows:
"ServicePoints": {
"APIServer": "http://localhost:5001",
"ImageServer": "http://localhost:5002",
}
Next, I created a HttpClientsFactory that will instantiate my 2 httpclients and hold them in a static Dictionary.
public class HttpClientsFactory : IHttpClientsFactory
{
public static Dictionary<string, HttpClient> HttpClients { get; set; }
private readonly ILogger _logger;
private readonly IOptions<ServerOptions> _serverOptionsAccessor;
public HttpClientsFactory(ILoggerFactory loggerFactory, IOptions<ServerOptions> serverOptionsAccessor) {
_logger = loggerFactory.CreateLogger<HttpClientsFactory>();
_serverOptionsAccessor = serverOptionsAccessor;
HttpClients = new Dictionary<string, HttpClient>();
Initialize();
}
private void Initialize()
{
HttpClient client = new HttpClient();
// ADD imageServer
var imageServer = _serverOptionsAccessor.Value.ImageServer;
client.BaseAddress = new Uri(imageServer);
HttpClients.Add("imageServer", client);
// ADD apiServer
var apiServer = _serverOptionsAccessor.Value.APIServer;
client.BaseAddress = new Uri(apiServer);
HttpClients.Add("apiServer", client);
}
public Dictionary<string, HttpClient> Clients()
{
return HttpClients;
}
public HttpClient Client(string key)
{
return Clients()[key];
}
}
Then, I created the interface that I can use when defining my DI later on. Notice that the HttpClientsFactory class inherits from this interface.
public interface IHttpClientsFactory
{
Dictionary<string, HttpClient> Clients();
HttpClient Client(string key);
}
Now I am ready to inject this into my Dependency container as follows in the Startup class under the ConfigureServices method.
// Add httpClient service
services.AddSingleton<IHttpClientsFactory, HttpClientsFactory>();
All is now set-up to start using this in my controller.
Firstly, I take in the dependency. To do this I created a private class property to hold it, then add it to the constructor signature and finish by assigning the incoming object to the local class property.
private IHttpClientsFactory _httpClientsFactory;
public AppUsersAdminController(IHttpClientsFactory httpClientsFactory)
{
_httpClientsFactory = httpClientsFactory;
}
Finally, we can now use the Factory to request a htppclient and execute a call. Below, an example where I request an image from the imageserver using the httpclientsfactory:
[HttpGet]
public async Task<ActionResult> GetUserPicture(string imgName)
{
// get imageserver uri
var imageServer = _optionsAccessor.Value.ImageServer;
// create path to requested image
var path = imageServer + "/imageuploads/" + imgName;
var client = _httpClientsFactory.Client("imageServer");
byte[] image = await client.GetByteArrayAsync(path);
return base.File(image, "image/jpeg");
}
Done!
I’ve tested this and it work great on my development environment. However, I am not sure if this is the best way to implement this. I remain with the following questions:
Is this solution thread safe? (according to the MS doc: ‘Any public static (Shared in Visual Basic) members of this type are thread safe.’)
Will this set-up be able to handle a heavy load without opening many separate connection?
What to do in ASP.Net core to handle the DNS problem described in ‘Singleton HttpClient? Beware of this serious behaviour and how to fix.’ located at http://byterot.blogspot.be/2016/07/singleton-httpclient-dns.html
Any other improvements or suggestions?
If using .net core 2.1 or higher, the best approach would be to use the new HttpClientFactory. I guess Microsoft realized all the issues people were having so they did the hard work for us. See below for how to set it up.
NOTE: Add a reference to Microsoft.Extensions.Http.
1 - Add a class that uses HttpClient
public interface ISomeApiClient
{
Task<HttpResponseMessage> GetSomethingAsync(string query);
}
public class SomeApiClient : ISomeApiClient
{
private readonly HttpClient _client;
public SomeApiClient (HttpClient client)
{
_client = client;
}
public async Task<SomeModel> GetSomethingAsync(string query)
{
var response = await _client.GetAsync($"?querystring={query}");
if (response.IsSuccessStatusCode)
{
var model = await response.Content.ReadAsJsonAsync<SomeModel>();
return model;
}
// Handle Error
}
}
2 - Register your clients in ConfigureServices(IServiceCollection services) in Startup.cs
var someApiSettings = Configuration.GetSection("SomeApiSettings").Get<SomeApiSettings>(); //Settings stored in app.config (base url, api key to add to header for all requests)
services.AddHttpClient<ISomeApiClient, SomeApiClient>("SomeApi",
client =>
{
client.BaseAddress = new Uri(someApiSettings.BaseAddress);
client.DefaultRequestHeaders.Add("api-key", someApiSettings.ApiKey);
});
3 - Use the client in your code
public class MyController
{
private readonly ISomeApiClient _client;
public MyController(ISomeApiClient client)
{
_client = client;
}
[HttpGet]
public async Task<IActionResult> GetAsync(string query)
{
var response = await _client.GetSomethingAsync(query);
// Do something with response
return Ok();
}
}
You can add as many clients and register as many as needed in your startup with services.AddHttpClient
Thanks to Steve Gordon and his post here for helping me use this in my code!
In answer to a question from #MuqeetKhan regarding using authentication with the httpclient request.
Firstly, my motivation to use DI and a factory was to allow me to extend my application easily to different and multiple API’s and have easy access to that throughout my code. It’s a template I hope to be able to reuse multiple times.
In the case of my ‘GetUserPicture’ controller decribed in the original question above, I indeed for simplicity reasons removed the authentication. Honestly however, I am still in doubt if I need it there to simply retrieve an image from the imageserver. Anyhow, in other controllers I definitely do need it, so…
I’ve implemented Identityserver4 as my authentication server. This provides me with the authentication on top of ASP Identity.
For authorization (using roles in this case), I implemented IClaimsTransformer in my MVC ‘and’ API projects (you can read more about this here at How to put ASP.net Identity Roles into the Identityserver4 Identity token).
Now, the moment I enter my controller I have an authenticated and authorized user for which I can retrieve an access token. I use this token to call my api which is of course calling the same instance of identityserver to verify if the user is authenticated.
The last step is to allow my API to verify if the user is authorized to call the requested api controller. In the request pipeline of the API using IClaimsTransformer as explained before, I retrieve the authorization of the calling user and add it to the incoming claims.
Note that in case of an MVC calling and API, I thus retrieve the authorization 2 times; once in the MVC request pipeline and once in the API request pipeline.
Using this set-up I am able to use my HttpClientsFactory with Authorization and Authentication.
On big security part I am missing is HTTPS of course. I hope I can somehow add it to my factory. I'll update it once I've implemented it.
As always, any suggestions are welcome.
Below an example where I upload an image to the Imageserver using authentication (user must be logged in and have role admin).
My MVC controller calling the ‘UploadUserPicture’:
[Authorize(Roles = "Admin")]
[HttpPost]
public async Task<ActionResult> UploadUserPicture()
{
// collect name image server
var imageServer = _optionsAccessor.Value.ImageServer;
// collect image in Request Form from Slim Image Cropper plugin
var json = _httpContextAccessor.HttpContext.Request.Form["slim[]"];
// Collect access token to be able to call API
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
// prepare api call to update image on imageserver and update database
var client = _httpClientsFactory.Client("imageServer");
client.DefaultRequestHeaders.Accept.Clear();
client.SetBearerToken(accessToken);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("image", json[0])
});
HttpResponseMessage response = await client.PostAsync("api/UserPicture/UploadUserPicture", content);
if (response.StatusCode != HttpStatusCode.OK)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return StatusCode((int)HttpStatusCode.OK);
}
API handling the user upload
[Authorize(Roles = "Admin")]
[HttpPost]
public ActionResult UploadUserPicture(String image)
{
dynamic jsonDe = JsonConvert.DeserializeObject(image);
if (jsonDe == null)
{
return new StatusCodeResult((int)HttpStatusCode.NotModified);
}
// create filname for user picture
string userId = jsonDe.meta.userid;
string userHash = Hashing.GetHashString(userId);
string fileName = "User" + userHash + ".jpg";
// create a new version number
string pictureVersion = DateTime.Now.ToString("yyyyMMddHHmmss");
// get the image bytes and create a memory stream
var imagebase64 = jsonDe.output.image;
var cleanBase64 = Regex.Replace(imagebase64.ToString(), #"^data:image/\w+;base64,", "");
var bytes = Convert.FromBase64String(cleanBase64);
var memoryStream = new MemoryStream(bytes);
// save the image to the folder
var fileSavePath = Path.Combine(_env.WebRootPath + ("/imageuploads"), fileName);
FileStream file = new FileStream(fileSavePath, FileMode.Create, FileAccess.Write);
try
{
memoryStream.WriteTo(file);
}
catch (Exception ex)
{
_logger.LogDebug(LoggingEvents.UPDATE_ITEM, ex, "Could not write file >{fileSavePath}< to server", fileSavePath);
return new StatusCodeResult((int)HttpStatusCode.NotModified);
}
memoryStream.Dispose();
file.Dispose();
memoryStream = null;
file = null;
// update database with latest filename and version
bool isUpdatedInDatabase = UpdateDatabaseUserPicture(userId, fileName, pictureVersion).Result;
if (!isUpdatedInDatabase)
{
return new StatusCodeResult((int)HttpStatusCode.NotModified);
}
return new StatusCodeResult((int)HttpStatusCode.OK);
}
For situations when you can't use DI:
using System.Net.Http;
public class SomeClass
{
private static readonly HttpClient Client;
static SomeClass()
{
var handler = new SocketsHttpHandler
{
// Sets how long a connection can be in the pool to be considered reusable (by default - infinite)
PooledConnectionLifetime = TimeSpan.FromMinutes(1),
};
Client = new HttpClient(handler, disposeHandler: false);
}
...
}
Reference https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0#alternatives-to-ihttpclientfactory

Using the subdomain as a parameter

I’ve got an ASP.net MVC (5.2) site that runs using several subdomains, where the name of the subdomain is the name of a client in my database. Basically what I want to do is use the subdomain as a variable within my action methods to allow me to get the correct data from my database.
I did something similar a few years back, but it’s messy and not intuitive, so was wondering if there’s a better way to do it than I was using before. Here’s what I did before:
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
Session["subdomain"] = GetSubDomain(Request.Url);
}
private static string GetSubDomain(Uri url) {
string host = url.Host;
if (host.Split('.').Length > 1) {
int index = host.IndexOf(".");
string subdomain = host.Substring(0, index);
if (subdomain != "www") {
return subdomain;
}
}
return null;
}
Which basically assigned a key to the session variable if the subdomain was anything other than "www", but I’m really not happy with this way of doing it as it relies on me knowing that the session might contain this magic value!
Ideally I’d like to be able to create an attribute that I can decorate my classes/methods with that would extract the subdomain and then allow me to include a "subdomain" parameter in my action method that would contain the value extracted by the attribute. Is that even possible?
If that can’t be done, is there a better way of doing what I’m doing now without having to rely on the session?
Thanks,
Dylan
Your right this doesn't need to be stored in Session and IMHO shouldn't be, I would refactor this out into its own class and use HttpContext.Current.
public interface ISubDomainProvider
{
string SubDomain { get; set; }
}
public class SubDomainProvider : ISubDomainProvider
{
public SubDomainProvider()
{
string host = HttpContext.Current.Request.Url.Host; // not checked (off the top of my head
if (host.Split('.').Length > 1)
{
int index = host.IndexOf(".");
string subdomain = host.Substring(0, index);
if (subdomain != "www")
{
SubDomain = subdomain;
}
}
}
public string SubDomain { get; set; }
}
You choose how to use it, if your using an IoC container it would just be a case of injecting this class into your controller via the constructor, I like this because it is easier to Mock and Unit Test. Of course you can still do this:
public class SomeController : Controller
{
private readonly ISubDomainProvider _subDomainProvider;
public SomeController()
{
_subDomainProvider = new SubDomainProvider();
}
}
You could even create you own abstract Controller Class:
public abstract class MyAbstractController : Controller
{
public MyAbstractController()
{
SubDomain = new SubDomainProvider();
}
protected string SubDomain {get; set; }
}
public class SomeController : MyAbstractController
{
public ActionResult SomeAction()
{
// access the subdomain by calling the base base.SubDomain
}
}
You could set the name in the Session on the Session_Start event in the global.asax, this means it would only happen one time and would persist for the duration of the users' session
public void Session_Start(object sender, EventArgs e)
{
Session["subdomain"] = GetSubDomain(Request.Url);
}
Looks like there’s a good way of doing what I’m after at:
ASP.NET MVC Pass object from Custom Action Filter to Action
It essentially uses the route data to pass a custom parameter to the action, and can also pass objects other than simple strings etc.
On the plus side it avoids using the session and relying on magic values, but on the downside it means processing the URL for every request, which probably isn’t a good idea if a database is involved.

Calling one ServiceStack 4 service from another with a file upload thrown in for fun

I have a working service in 4.0.31 that looks like this:
public object Post(MyDTO request)
{
foreach (var uploadedFile in base.Request.Files)
{
... do something ...
}
return new MyDTOResponse();
}
Everything works great, and I'm happy!
But now, I want to call the same service method from within another service, the way to do this apparently is:
public object Post(MyOtherDTO request)
{
var myService = base.ResolveService<MyService>();
// now I call some new method I wrote to bypass the file upload part, since
// myService.Post() doesn't know about the file upload part
var myResponse = myService.NewMethodThatLetsMePassAStreamToTheOtherService(streamData);
... do other stuff...
return new MyOtherDTOResponse();
}
While I'm not unhappy with this, it does create a hard dependency between the two services, so I'm not thrilled like I usually am with ServiceStack!
Is there a more elegant way of putting this together? I'm probably just missing something really, really obvious...
I'm not 100% clear on what the issue is, if it's how to share logic between services? then you could pull common logic out of each service class and reference the shared code in both Services.
If no dependencies are required I'll refactor the shared code behind re-usable extension methods.
If dependencies are required I will refactor it behind a shared logic class that's a dependency in both Services, see the IGreeter example in the sharing logic between MVC and ServiceStack answer:
public class MyService1 : Service
{
public ISharedDep SharedDep { get; set]
public object Any(Request1 request)
{
//...
}
}
public class MyService2 : Service
{
public ISharedDep SharedDep { get; set]
public object Any(Request2 request)
{
//...
}
}
Shared logic using Request Context using base class
If it's common code used by many Services that requires the base.Request context than you could move it to a common Service base class:
public class MyServiceBase : Service
{
public ISharedDep SharedDep { get; set]
public object SharedMethod(object request)
{
//...
}
}
public class MyServices1 : MyServiceBase { ... }
public class MyServices2 : MyServiceBase { ... }
Shared logic using Request Context using Extension method
If you prefer not to use a base class, this can be re-factored behind an extension method as well:
public static void MyServiceExtensions
{
public static object SharedMethod(this IServicBase service, object request)
{
var sharedDep = service.TryResolve<ISharedDep>();
return sharedDep.SharedMethodWithRequestCtx(request, service.Request);
}
}
Loose Coupling by executing a Request DTO
If the issue is about a loose-coupled way to call Services without a reference to the implementation itself you can execute the Request DTO with the ServiceController:
public class MyService : Service
{
public object Any(Request requestDto)
{
var altDto = new AltRequest { Id = requestDto.Id };
var response = HostContext.ServiceController.Execute(altDto, base.Request);
//...
}
}
Note: this API is available as base.ExecuteRequest(requestDto) in v4.0.32+.
Uploading Files to a HTTP Service In Memory
If the issue is instead how to execute a Service that handles file uploads, there's an example in the embedded version of HttpBenchmarks showing how to call a Service that processes HTTP File uploads with a custom Request Context that uses local FileSystem files instead:
using (var admin = Resolve<AdminServices>())
{
//...
var dir = new FileSystemVirtualPathProvider(this, Config.WebHostPhysicalPath);
var files = dir.GetAllMatchingFiles("*.txt")
.Concat(dir.GetAllMatchingFiles("*.zip"));
admin.Request = new BasicRequest
{
Files = files.Map(x => new HttpFile {
ContentLength = x.Length,
ContentType = MimeTypes.GetMimeType(x.Name),
FileName = x.Name,
InputStream = x.OpenRead(),
} as IHttpFile).ToArray()
};
if (admin.Request.Files.Length > 0)
{
admin.Post(new UploadTestResults
{
TestPlanId = 1,
TestRunId = testRun.Id,
CreateNewTestRuns = true,
});
}
}

Categories

Resources