TypeScript - retain scope in event listener - scope

I am converting my AS3 codebase to TypeScript and run into this error:
AS3 code:
private function loadDataXml(assetsXml : String) : void {
var loader : URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, handleDataLoaded);
loader.load(new URLRequest(assetsXml));
}
private function handleDataLoaded(event : Event) : void {
var xml_data : XML = new XML(event.target.data);
parseData(xml_data);
.........
}
private function parseData(xml_data : XML) : void {
......
}
TypeScript code
private loadDataXml(assetsXml : string) {
var xmlRequest:XMLHttpRequest = new XMLHttpRequest();
xmlRequest.addEventListener("load",this.handleDataLoaded, false);
xmlRequest.open("GET", assetsXml, false);
xmlRequest.setRequestHeader("Content-Type", "text/xml");
xmlRequest.send(null);
}
private handleDataLoaded(evt:Event) {
var xmlDoc:Document = (<XMLHttpRequest> evt.target).responseXML;
this.parseXMLData(xmlDoc);
......
}
private parseData(xmlDoc:Document):void {
......
}
and I get this error "Uncaught TypeError: Object # has no method 'parseData'" because of this line xmlRequest.addEventListener.....
I have tried using arrow function but still couldn't fix it (and i don't think I use it correctly)

When you need to pass functions around use the new lambda syntax for member variables (introduced in TypeScript 0.9.1):
private loadDataXml(assetsXml : string) {
var xmlRequest:XMLHttpRequest = new XMLHttpRequest();
// you are passing a member function Use lambda to define this function:
xmlRequest.addEventListener("load",this.handleDataLoaded, false);
xmlRequest.open("GET", assetsXml, false);
xmlRequest.setRequestHeader("Content-Type", "text/xml");
xmlRequest.send(null);
}
private handleDataLoaded = (evt:Event) => { // Since you want to pass this around
var xmlDoc:Document = (<XMLHttpRequest> evt.target).responseXML;
this.parseXMLData(xmlDoc); // you will get the correct this here
......
}
private parseData(xmlDoc:Document):void {
......
}

Try binding to the scope
xmlRequest.addEventListener("load",this.handleDataLoaded.bind(this), false);

Related

Testing a Multipart file upload Azure Function

So I have written a simple Azure Function (AF) that accepts (via Http Post method) an IFormCollection, loops through the file collection, pushes each file into an Azure Blob storage container and returns the url to each file.
The function itself works perfectly when I do a single file or multiple file post through Postman using the 'multipart/form-data' header. However when I try to post a file through an xUnit test, I get the following error:
System.IO.InvalidDataException : Multipart body length limit 16384 exceeded.
I have searched high and low for a solution, tried different things, namely;
Replicating the request object to be as close as possible to Postmans request.
Playing around with the 'boundary' in the header.
Setting 'RequestFormLimits' on the function.
None of these have helped so far.
The details are the project are as follows:
Azure Function v3: targeting .netcoreapp3.1
Startup.cs
public class Startup : FunctionsStartup
{
public IConfiguration Configuration { get; private set; }
public override void Configure(IFunctionsHostBuilder builder)
{
var x = builder;
InitializeConfiguration(builder);
builder.Services.AddSingleton(Configuration.Get<UploadImagesAppSettings>());
builder.Services.AddLogging();
builder.Services.AddSingleton<IBlobService,BlobService>();
}
private void InitializeConfiguration(IFunctionsHostBuilder builder)
{
var executionContextOptions = builder
.Services
.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>()
.Value;
Configuration = new ConfigurationBuilder()
.SetBasePath(executionContextOptions.AppDirectory)
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json", optional: true)
.AddEnvironmentVariables()
.Build();
}
}
UploadImages.cs
public class UploadImages
{
private readonly IBlobService BlobService;
public UploadImages(IBlobService blobService)
{
BlobService = blobService;
}
[FunctionName("UploadImages")]
[RequestFormLimits(ValueLengthLimit = int.MaxValue,
MultipartBodyLengthLimit = 60000000, ValueCountLimit = 10)]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "images")] HttpRequest req)
{
List<Uri> returnUris = new List<Uri>();
if (req.ContentLength == 0)
{
string badResponseMessage = $"Request has no content";
return new BadRequestObjectResult(badResponseMessage);
}
if (req.ContentType.Contains("multipart/form-data") && req.Form.Files.Count > 0)
{
foreach (var file in req.Form.Files)
{
if (!file.IsValidImage())
{
string badResponseMessage = $"{file.FileName} is not a valid/accepted Image file";
return new BadRequestObjectResult(badResponseMessage);
}
var uri = await BlobService.CreateBlobAsync(file);
if (uri == null)
{
return new ObjectResult($"Could not blob the file {file.FileName}.");
}
returnUris.Add(uri);
}
}
if (!returnUris.Any())
{
return new NoContentResult();
}
return new OkObjectResult(returnUris);
}
}
Exception Thrown:
The below exception is thrown at the second if statement above, when it tries to process req.Form.Files.Count > 0, i.e.
if (req.ContentType.Contains("multipart/form-data") && req.Form.Files.Count > 0) {}
Message:
System.IO.InvalidDataException : Multipart body length limit 16384 exceeded.
Stack Trace:
MultipartReaderStream.UpdatePosition(Int32 read)
MultipartReaderStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
StreamHelperExtensions.DrainAsync(Stream stream, ArrayPool`1 bytePool, Nullable`1 limit, CancellationToken cancellationToken)
MultipartReader.ReadNextSectionAsync(CancellationToken cancellationToken)
FormFeature.InnerReadFormAsync(CancellationToken cancellationToken)
FormFeature.ReadForm()
DefaultHttpRequest.get_Form()
UploadImages.Run(HttpRequest req) line 42
UploadImagesTests.HttpTrigger_ShouldReturnListOfUploadedUris(String fileNames)
xUnit Test Project: targeting .netcoreapp3.1
Over to the xUnit Test project, basically I am trying to write an integration test. The project references the AF project and has the following classes:
TestHost.cs
public class TestHost
{
public TestHost()
{
var startup = new TestStartup();
var host = new HostBuilder()
.ConfigureWebJobs(startup.Configure)
.ConfigureServices(ReplaceTestOverrides)
.Build();
ServiceProvider = host.Services;
}
public IServiceProvider ServiceProvider { get; }
private void ReplaceTestOverrides(IServiceCollection services)
{
// services.Replace(new ServiceDescriptor(typeof(ServiceToReplace), testImplementation));
}
private class TestStartup : Startup
{
public override void Configure(IFunctionsHostBuilder builder)
{
SetExecutionContextOptions(builder);
base.Configure(builder);
}
private static void SetExecutionContextOptions(IFunctionsHostBuilder builder)
{
builder.Services.Configure<ExecutionContextOptions>(o => o.AppDirectory = Directory.GetCurrentDirectory());
}
}
}
TestCollection.cs
[CollectionDefinition(Name)]
public class TestCollection : ICollectionFixture<TestHost>
{
public const string Name = nameof(TestCollection);
}
HttpRequestFactory.cs: To create Http Post Request
public static class HttpRequestFactory
{
public static DefaultHttpRequest Create(string method, string contentType, Stream body)
{
var request = new DefaultHttpRequest(new DefaultHttpContext());
var contentTypeWithBoundary = new MediaTypeHeaderValue(contentType)
{
Boundary = $"----------------------------{DateTime.Now.Ticks.ToString("x")}"
};
var boundary = MultipartRequestHelper.GetBoundary(
contentTypeWithBoundary, (int)body.Length);
request.Method = method;
request.Headers.Add("Cache-Control", "no-cache");
request.Headers.Add("Content-Type", contentType);
request.ContentType = $"{contentType}; boundary={boundary}";
request.ContentLength = body.Length;
request.Body = body;
return request;
}
private static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
if (string.IsNullOrWhiteSpace(boundary.Value))
{
throw new InvalidDataException("Missing content-type boundary.");
}
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException(
$"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary.Value;
}
}
The MultipartRequestHelper.cs class is available here
And Finally the Test class:
[Collection(TestCollection.Name)]
public class UploadImagesTests
{
readonly UploadImages UploadImagesFunction;
public UploadImagesTests(TestHost testHost)
{
UploadImagesFunction = new UploadImages(testHost.ServiceProvider.GetRequiredService<IBlobService>());
}
[Theory]
[InlineData("testfile2.jpg")]
public async void HttpTrigger_ShouldReturnListOfUploadedUris(string fileNames)
{
var formFile = GetFormFile(fileNames);
var fileStream = formFile.OpenReadStream();
var request = HttpRequestFactory.Create("POST", "multipart/form-data", fileStream);
var response = (OkObjectResult)await UploadImagesFunction.Run(request);
//fileStream.Close();
Assert.True(response.StatusCode == StatusCodes.Status200OK);
}
private static IFormFile GetFormFile(string fileName)
{
string fileExtension = fileName.Substring(fileName.IndexOf('.') + 1);
string fileNameandPath = GetFilePathWithName(fileName);
IFormFile formFile;
var stream = File.OpenRead(fileNameandPath);
switch (fileExtension)
{
case "jpg":
formFile = new FormFile(stream, 0, stream.Length,
fileName.Substring(0, fileName.IndexOf('.')),
fileName)
{
Headers = new HeaderDictionary(),
ContentType = "image/jpeg"
};
break;
case "png":
formFile = new FormFile(stream, 0, stream.Length,
fileName.Substring(0, fileName.IndexOf('.')),
fileName)
{
Headers = new HeaderDictionary(),
ContentType = "image/png"
};
break;
case "pdf":
formFile = new FormFile(stream, 0, stream.Length,
fileName.Substring(0, fileName.IndexOf('.')),
fileName)
{
Headers = new HeaderDictionary(),
ContentType = "application/pdf"
};
break;
default:
formFile = null;
break;
}
return formFile;
}
private static string GetFilePathWithName(string filename)
{
var outputFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
return $"{outputFolder.Substring(0, outputFolder.IndexOf("bin"))}testfiles\\{filename}";
}
}
The test seems to be hitting the function and req.ContentLength does have a value. Considering this, could it have something to do with the way the File Streams are being managed? Perhaps not the right way?
Any inputs on this would be greatly appreciated.
Thanks
UPDATE 1
As per this post, I have also tried setting the ValueLengthLimit and MultipartBodyLengthLimit in the Startup of the Azure Function and/or the Test Project as opposed to attributes on the Azure Function. The exception then changed to:
"The inner stream position has changed unexpectedly"
Following this, I then set the fileStream position in the test project to SeekOrigin.Begin. I started getting the same error:
"Multipart body length limit 16384 exceeded."
It took me a 50km bike ride and a good nights sleep but I finally figured this one out :-).
The Azure function (AF) accepts an HttpRequest object as a parameter with the name of 'req' i.e.
public async Task Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "images")] HttpRequest req)
The hierarchy of the files object in the HttpRequest object (along with the parameter names) is as follows:
HttpRequest -> req
FormCollection -> Form
FormFileCollection -> Files
This is what the AF accepts and one would access the files collection by using req.Form.Files
In my test case, instead of posting a FormCollection object, I was trying to post a Stream of a file to the Azure Function.
var formFile = GetFormFile(fileNames);
var fileStream = formFile.OpenReadStream();
var request = HttpRequestFactory.Create("POST", "multipart/form-data", fileStream);
As a result of this, req.Form had a Stream value that it could not interpret and the req.Form.Files was raising an exception.
In order to rectify this, I had to do the following:
Revert all changes made as part of UPDATE 1. This means that I removed the 'RequestFormLimits' settings from the Startup file and left them as attributes on the functions Run method.
Instantiate a FormFileCollection object and add the IFormFile to it
Instantiate a FormCollection object using this FormFileCollection as a parameter.
Add the FormCollection to the request object.
To achieve the above, I had to make the following changes in code.
Change 'Create' method in the HttpRequestFactory
public static DefaultHttpRequest Create(string method, string contentType, FormCollection formCollection)
{
var request = new DefaultHttpRequest(new DefaultHttpContext());
var boundary = $"----------------------------{DateTime.Now.Ticks.ToString("x")}";
request.Method = method;
request.Headers.Add("Cache-Control", "no-cache");
request.Headers.Add("Content-Type", contentType);
request.ContentType = $"{contentType}; boundary={boundary}";
request.Form = formCollection;
return request;
}
Add a private static GetFormFiles() method
I wrote an additional GetFormFiles() method that calls the existing GetFormFile() method, instantiate a FormFileCollection object and add the IFormFile to it. This method in turn returns a FormFileCollection.
private static FormFileCollection GetFormFiles(string fileNames)
{
var formFileCollection = new FormFileCollection();
foreach (var file in fileNames.Split(','))
{
formFileCollection.Add(GetFormFile(file));
}
return formFileCollection;
}
Change the Testmethod
The test method calls the GetFormFiles() to get a FormFileCollection then
instantiates a FormCollection object using this FormFileCollection as a parameter and then passes the FormCollection object as a parameter to the HttpRequest object instead of passing a Stream.
[Theory]
[InlineData("testfile2.jpg")]
public async void HttpTrigger_ShouldReturnListOfUploadedUris(string fileNames)
{
var formFiles = GetFormFiles(fileNames);
var formCollection = new FormCollection(null, formFiles);
var request = HttpRequestFactory.Create("POST", "multipart/form-data", formCollection);
var response = (OkObjectResult) await UploadImagesFunction.Run(request);
Assert.True(response.StatusCode == StatusCodes.Status200OK);
}
So in the end the issue was not really with the 'RequestFormLimits' but rather with the type of data I was submitting in the POST message.
I hope this answer provides a different perspective to someone that comes across the same error message.
Cheers.

Unit testing that the swagger doc is correct without starting a server

I'd like to test that the swagger document is correct for my application (mainly, because I've added a strategy to generate custom OperationIds and I want to ensure they are correctly unique)
However, the only solutions I found are all using a "real" server (cf https://stackoverflow.com/a/52521454/1545567), which is not an option for me since I do not have the database, message bus, etc... when I launch the unit tests in CI...
At the moment, I have the following but it always generate 0 paths and 0 models ...
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using SampleCheckIn;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Linq;
using Xunit;
using SampleCheckIn.Def;
using Service.Utils;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
namespace D4Interop.Tests
{
public class TmpTest
{
[Fact]
public void Tmp()
{
var controllers = typeof(Startup).Assembly.GetTypes().Where(x => IsController(x)).ToList();
controllers.Any().Should().BeTrue();
var services = new ServiceCollection();
controllers.ForEach(c => services.AddScoped(c));
services.AddLogging(logging => logging.AddConsole());
services.AddControllers(); //here, I've also tried AddMvcCore and other ASP methods...
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("api", new OpenApiInfo { Title = Constants.SERVICE_NAME, Version = "_", Description = Constants.SERVICE_DESC });
//c.OperationFilter<SwaggerUniqueOperationId>(); //this is my filter that ensures the operationId is unique
c.CustomOperationIds(apiDesc =>
{
return apiDesc.TryGetMethodInfo(out var methodInfo) ? methodInfo.Name : null;
});
});
services.AddSingleton<IWebHostEnvironment>(new FakeWebHostEnvironment());
var serviceProvider = services.BuildServiceProvider();
var swaggerProvider = serviceProvider.GetRequiredService<ISwaggerProvider>();
var swagger = swaggerProvider.GetSwagger("api");
swagger.Should().NotBeNull();
swagger.Paths.Any().Should().BeTrue();
}
private bool IsController(Type x)
{
return typeof(Microsoft.AspNetCore.Mvc.ControllerBase).IsAssignableFrom(x);
}
}
internal class FakeWebHostEnvironment : IWebHostEnvironment
{
public FakeWebHostEnvironment()
{
}
public IFileProvider WebRootFileProvider { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public string WebRootPath { get => "/root"; set => throw new NotImplementedException(); }
public string EnvironmentName { get => "dev"; set => throw new NotImplementedException(); }
public string ApplicationName { get => "app"; set => throw new NotImplementedException(); }
public string ContentRootPath { get => "/"; set => throw new NotImplementedException(); }
public IFileProvider ContentRootFileProvider { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
}
}
Ok, I've finally found that I just need to mix the linked answer with my code :
[Fact]
public async Task TestSwagger()
{
var server = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(options => { options.UseStartup<Startup>(); })
.Build();
var swagger = server.Services
.GetRequiredService<ISwaggerProvider>()
.GetSwagger("xxx"); //xxx should be the name of your API
swagger.Should().NotBeNull();
swagger.Paths.Any().Should().BeTrue();
swagger.Components.Schemas.Should().NotBeNull();
}

How to MOQ a repository and ensure that the method is called using VerifyAll()?

This is the repository class
using System;
using System.Collections.Generic;
using Dealer.Rails.Common.Utils;
using Dealer.Rails.Repository.Entities.C3;
using Dealer.Rails.Repository.Repositories.C3.Interfaces;
using Dealer.Rails.Repository.Repositories.Soar;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Dealer.Rails.Repository.Repositories.C3
{
public class VehicleStagingRepository : BaseRepository, IVehicleStagingRepository
{
public VehicleStagingRepository(ILogger<VehicleStagingRepository> logger)
: base(logger)
{
}
public void SaveVehicleStaging(List<ImportStageVh> vehicleStagingRecords, C3Context c3Context)
{
using (var transaction = c3Context.Database.BeginTransaction())
{
var vehicleStagingRecordBeforeSaving = new ImportStageVh();
try
{
foreach (var vehicleStagingRecord in vehicleStagingRecords)
{
vehicleStagingRecordBeforeSaving = vehicleStagingRecord;
c3Context.ImportStageVh.Add(vehicleStagingRecord);
c3Context.SaveChanges();
}
}
catch (TimeoutException tex)
{
transaction.Rollback();
_logger.LogError(LoggingEvents.SaveEmployeeStaging, tex.Message);
throw;
}
catch (Exception ex)
{
transaction.Rollback();
_logger.LogError(LoggingEvents.SaveEmployeeStaging, ex, JsonConvert.SerializeObject(vehicleStagingRecordBeforeSaving));
throw new Exception(vehicleStagingRecordBeforeSaving.ToString(),ex);
}
transaction.Commit();
}
}
}
}
This is the Xunit Test Cases using MOQ.
using Dealer.Rails.Repository.Entities.C3;
using Dealer.Rails.Repository.Repositories.C3;
using Dealer.Rails.Repository.Repositories.C3.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using System.Collections.Generic;
using Xunit;
namespace Dealer.Rails.Repository.Tests.Repositories.Rails
{
public class VehicleStagingRepositoryTests
{
[Fact]
[Trait("Repositories", "C3")]
public void Should_Dump_Records_To_Vehicle_Staging_Dump()
{
// Arrange
var optionsBuilder = new DbContextOptionsBuilder<C3Context>();
optionsBuilder.UseSqlServer("Data Source=dev.c3.dealertrack.corp;Initial Catalog=c3_bmc;Integrated Security=True;MultipleActiveResultSets=True;Application Name=EntityFramework");
var mockLogger = new Mock<ILogger<VehicleStagingRepository>>();
var mockC3Context = new C3Context(optionsBuilder.Options);
// var vehicleStagingRecords = new List<ImportStageVh>();
var mockRepository = new Mock<IVehicleStagingRepository>();
mockRepository.Setup(x => x.SaveVehicleStaging(It.IsAny<List<ImportStageVh>>(), mockC3Context));
// System under test is TemplateColumnRepository
var sut = new VehicleStagingRepository(mockLogger.Object);
// Act
sut.SaveVehicleStaging(new List<ImportStageVh>(), mockC3Context);
// Assert
mockRepository.Verify();
// mockRepository.Verify(m=>m.SaveVehicleStaging(vehicleStagingRecords,mockC3Context),Times.Once,"Failed to call SaveVehicleStaging method");
}
}
}
MOQ should allow me to independently test the repository. The issues that I am facing are
In the line optionsBuilder.UseSqlServer.. If use optionsBuilder.UseSqlServer("Some dummy connection string"); The code throws an exception. It my opinion it should not.
In the line mockRepository.Verify();, If use mockRepository.VerifyAll(), it fails at line
3. Even the next commented line is throwing error. In my opinions it should have executed SaveStagingVehicle method atleast once.
Please guide me in resolving these issues.

WebAPI can't call Put method

I have the following code on server:
public class UploadController : ApiController
{
public void Put(string filename, string description)
{
...
}
public void Put()
{
...
}
and try to call it from client:
var clientDescr = new HttpClient();
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("filename", "test"));
postData.Add(new KeyValuePair<string, string>("description", "100"));
HttpContent contentDescr = new FormUrlEncodedContent(postData);
clientDescr.PutAsync("http://localhost:8758/api/upload", contentDescr).ContinueWith(
(postTask) =>
{
postTask.Result.EnsureSuccessStatusCode();
});
but this code calls second put method (without parameters). Why and how to call first put method correctly?
You have several options here:
You can either choose to pass the parameters in the query string, by just changing the URI to:
http://localhost:8758/api/upload?filename=test&description=100
or you can have Web API parse the form data for you by changing your action to look like this:
public void Put(FormDataCollection formData)
{
string fileName = formData.Get("fileName");
string description = formData.Get("description");
}
You can also choose to create a class that has a fileName and a description property and use that as your parameter and Web API should be able to bind it correctly for you.

How can I query an AppHost for a specific route?

Basically I have following architecture:
Website-project (1)
Domain-project (2)
Api-project (3)
Dependencies:
1 uses 2 and 3
3 uses 2
2 uses nothing
In my Api-project I define a concrete implementation of ServiceStack.Webhost.Endpoints.AppHostBase, eg ApiAppHost:
public sealed class ApiAppHost : AppHostBase
{
private ApiAppHost()
: base("Description", typeof (ApiAppHost).Assembly) {}
public override void Configure(Container container)
{
this.SetConfig(new EndpointHostConfig
{
ServiceStackHandlerFactoryPath = "api"
});
this.Routes.Add<Foo>("/foo", "POST");
}
public static void Initialize()
{
var instance = new ApiAppHost();
instance.Init();
}
}
This is pretty straight-forward.
Now I want to query my this.Routes (in combination with EndpointHostConfig.ServiceStackHandlerFactoryPath) from my Website-project to get the specific path for Foo.
How can I do that without creating an interceptor on my own? Does ServiceStack.Net provide anything which fits?
Currently I am doing something like this
public static class AppHostBaseExtensions
{
public static string GetUrl<TRequest>(this AppHostBase appHostBase)
{
var requestType = typeof (TRequest);
return appHostBase.GetUrl(requestType);
}
public static string GetUrl(this AppHostBase appHostBase, Type requestType)
{
var endpointHostConfig = appHostBase.Config;
var serviceStackHandlerFactoryPath = endpointHostConfig.ServiceStackHandlerFactoryPath;
var serviceRoutes = appHostBase.Routes as ServiceRoutes;
if (serviceRoutes == null)
{
throw new NotSupportedException("Property Routes of AppHostBase is not of type ServiceStack.ServiceHost.ServiceRoutes");
}
var restPaths = serviceRoutes.RestPaths;
var restPath = restPaths.FirstOrDefault(arg => arg.RequestType == requestType);
if (restPath == null)
{
return null;
}
var path = restPath.Path;
var virtualPath = "~/" + string.Concat(serviceStackHandlerFactoryPath, path); // bad, i know, but combining with 2 virtual paths ...
var absolutePath = VirtualPathUtility.ToAbsolute(virtualPath);
return absolutePath;
}
}
I know that it is wrong because of many issues (path-combining, not taking rest-paths into account, not taking rest-paths with placeholders into account), but it works as a start ...
Edit:
And this only works, if you register the route within Configure(Container) of your AppHostBase-implementation. It wont't work with the RestServiceAttribute-attribute ...

Resources