using System;
using System.Text;
using Microsoft.Web.Administration;
internal static class Sample
{
private static void Main()
{
using (ServerManager serverManager = new ServerManager())
{
Configuration config = serverManager.GetApplicationHostConfiguration();
ConfigurationSection sitesSection = config.GetSection("system.applicationHost/sites");
ConfigurationElementCollection sitesCollection = sitesSection.GetCollection();
ConfigurationElement siteElement = FindElement(sitesCollection, "site", "name", #"Contoso");
if (siteElement == null) throw new InvalidOperationException("Element not found!");
ConfigurationElementCollection siteCollection = siteElement.GetCollection();
ConfigurationElement applicationElement = siteCollection.CreateElement("application");
applicationElement["path"] = #"/ShoppingCart";
ConfigurationElementCollection applicationCollection = applicationElement.GetCollection();
ConfigurationElement virtualDirectoryElement = applicationCollection.CreateElement("virtualDirectory");
virtualDirectoryElement["path"] = #"/";
virtualDirectoryElement["physicalPath"] = #"C:\Inetpub\Contoso\ShoppingCart";
applicationCollection.Add(virtualDirectoryElement);
siteCollection.Add(applicationElement);
serverManager.CommitChanges();
}
}
private static ConfigurationElement FindElement(ConfigurationElementCollection collection, string elementTagName, params string[] keyValues)
{
foreach (ConfigurationElement element in collection)
{
if (String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase))
{
bool matches = true;
for (int i = 0; i < keyValues.Length; i += 2)
{
object o = element.GetAttributeValue(keyValues[i]);
string value = null;
if (o != null)
{
value = o.ToString();
}
if (!String.Equals(value, keyValues[i + 1], StringComparison.OrdinalIgnoreCase))
{
matches = false;
break;
}
}
if (matches)
{
return element;
}
}
}
return null;
}
}
My angular client works with IIS. All GET requests are well served by IIS.
Every http POST request should be converted to TCP request that will be sent to another PC.
Should I create an ASP.net application for this purpose ?
Can I use another architecture(but ASP.net) that will work with IIS ?
This external application has no GUI. Only simple console (which is not mandatory).
Thank you,
Zvika
Related
I have created a library project for writing logs into ApplicationInsights as well as table storage and is being consumed my different other WebAPI projects. But due to some reason the logs are not getting logged in Application Insights but it works with table storage.
private void AddTelemetryTarget(string instrumentationKey, LoggerEnumerations.LogLevel minLogLevel, LoggingConfiguration config)
{
try
{ ConfigurationItemFactory.Default.Targets.RegisterDefinition("ApplicationInsightsTarget", typeof(ApplicationInsightsTarget));
ApplicationInsightsTarget aiTarget = new ApplicationInsightsTarget();
aiTarget.InstrumentationKey = instrumentationKey;
aiTarget.Name = "ai";
var wrapper = new AsyncTargetWrapper(aiTarget, 5000, AsyncTargetWrapperOverflowAction.Grow);
config.AddTarget("TelemetryAsyncWrapper", wrapper);
//Applying logging rules.
LoggingRule rule = new LoggingRule("*", ConvertLogType(minLogLevel), aiTarget);
config.LoggingRules.Add(rule);
}
catch
{ }
}
private LogLevel ConvertLogType(LoggerEnumerations.LogLevel type)
{
switch (type)
{
case LoggerEnumerations.LogLevel.Error: return LogLevel.Error;
case LoggerEnumerations.LogLevel.Info: return LogLevel.Info;
case LoggerEnumerations.LogLevel.Warn: return LogLevel.Warn;
default: return LogLevel.Trace;
}
}
public async Task Log(string message, LoggerEnumerations.LogLevel type, Dictionary<string, string> customParams, Exception ex = null, bool isPayload = false)
{
LogEventInfo eventInfo = PopulateEventInfo(message, type, customParams, ex);
if (!isPayload)
{
_logger.Log(eventInfo);
}
else
{
_payloadLogger.Log(eventInfo);
}
}
private LogEventInfo PopulateEventInfo(string message, LoggerEnumerations.LogLevel type, Dictionary<string, string> customParams, Exception ex = null)
{
LogEventInfo eventInfo = new LogEventInfo();
eventInfo.Level = ConvertLogType(type);
eventInfo.Message = message;
eventInfo.LoggerName = this.GetType().ToString();
if (ex != null)
{
eventInfo.Exception = ex;
}
else if (eventInfo.Level == LogLevel.Error)
{
eventInfo.Exception = new Exception(message);
}
//Adding custom properties to LogEventInfo to display in Application insight
if (customParams != null)
{
foreach (KeyValuePair<string, string> param in customParams)
{
eventInfo.Properties.Add(param.Key, param.Value);
}
}
return eventInfo;
}
Version of Nuget packages are
Microsoft.ApplicationInsights.NLogTarget : 2.13.1
NLog : 4.6.8
Thanks
I added Application Insights as Connected Services and I removed the Instrumentation Key from ApplicationInsights.config file and when registering the nlog target I used instrumentation key from my web.config file and it started working.
I have a web api call that I recently added to my app. I returns a complete list of all countries, states and cities in the app (currently 486 rows) I perform this call when all of the reference data for my application loads (I have a base loading page and call the function in my startup class to load all the data there). The challenge is that the call to get all my countries.... hangs and eventually I get "The operation was canceled" error. If I modify my stored procedure that selects the data from the database on the server to only return say 20 rows, it runs fine. Any suggestions?
Below is the code from the startup class:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace GBarScene
{
class StartUpClass
{
public event GeneralDataLoad BaseDataLoadComplete;
public async Task<GBSStartUpEventArgs> ProcessStartup()
{
GBSStartUpEventArgs lobj_EventArgs;
lobj_EventArgs = new GBSStartUpEventArgs();
App.InStartUpDataLoad = true;
try
{
if (!App.IsGeolocationEnabled)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoLocationServicesMessage");
}
else if (!App.InternetIsAvailable)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoInternetConnectionFound");
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
}
else
{
Debug.WriteLine("Process StartUp");
await Task.Delay(500);
//Reset values
ViewModelObjects.DayOfWeek.DataLoadProcessed = false;
ViewModelObjects.Languages.DataLoadProcessed = false;
if (await ViewModelObjects.DayOfWeek.LoadData() == false)
// //try it once more
await ViewModelObjects.DayOfWeek.LoadData();
Debug.WriteLine("GBar After DayofWeek Load");
await ViewModelObjects.Languages.LoadData();
Debug.WriteLine("GBar After Languages Load");
if ((ge_AppMode)ViewModelObjects.AppSettings.AppMode == ge_AppMode.CitySelected)
{
//We need to reload the NearbyCities and set the selected one
await ViewModelObjects.NearbyCities.LoadData();
}
Debug.WriteLine("Before load of coutries");
await ViewModelObjects.CountryStateCity.LoadData();
Debug.WriteLine("After load of coutries");
Debug.WriteLine("Count: " + ViewModelObjects.CountryStateCity.CountryItems_ForList.Count.ToString());
ViewModelObjects.NumberOfResults.LoadData();
ViewModelObjects.Perspectives.LoadData();
ViewModelObjects.SearchRadiuses.LoadData();
ViewModelObjects.UseMetric.LoadData();
while (!ViewModelObjects.DayOfWeek.DataLoadProcessed && !ViewModelObjects.Languages.DataLoadProcessed && !App.IsGeolocationEnabled)
{
await Task.Delay(100);
}
if (App.BaseDataLoadError)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("ErrorLoadingReferenceData");
}
}
Debug.WriteLine("StartUp Process Ended");
BaseDataLoadComplete(this, lobj_EventArgs);
}
catch (Exception ex)
{
App.ProcessException(ex);
}
App.InStartUpDataLoad = false;
return lobj_EventArgs;
}
}
}
This is the helper class that makes all the WebAPI calls:
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace GBarScene
{
public class WebAPICaller: IDisposable
{
HttpClient iobj_HTTPClient = null;
public void Dispose()
{
if (iobj_HTTPClient != null)
iobj_HTTPClient.Dispose();
}
public async Task<string> HTTPGetWebServiceAsync(string ps_URI)
{
string ls_Response = "";
string ls_JSONData = "";
string ls_Prefix = "";
try
{
iobj_HTTPClient = await GetClient();
switch (Device.RuntimePlatform)
{
case Device.Android:
ls_Prefix = App.APIStandardPrefix;
break;
//case Device.Android:
// ls_Prefix = App.APISecurePrefix;
// break;
//case Device.Windows:
//case Device.WinPhone:
// ls_Prefix = App.APISecurePrefix;
// break;
default:
ls_Prefix = App.APISecurePrefix;
break;
}
Debug.WriteLine("before api call");
iobj_HTTPClient.BaseAddress = new Uri(ls_Prefix);
ls_JSONData = await iobj_HTTPClient.GetStringAsync(ps_URI);
Debug.WriteLine("after api call");
ls_Response = System.Net.WebUtility.HtmlDecode(ls_JSONData);
}
catch (Exception ex)
{
Debug.WriteLine("api call error");
App.ProcessException(ex);
}
return ls_Response;
}
public async Task<bool> HTTPPostWebService(string ps_URI, object pobj_BodyObject)
{
HttpResponseMessage lobj_HTTPResponse = null;
bool lb_Response = false;
HttpContent lobj_Content = null;
try
{
if (iobj_HTTPClient != null)
iobj_HTTPClient = await GetClient();
iobj_HTTPClient.BaseAddress = new Uri(App.APISecurePrefix);
lobj_Content = new StringContent(JsonConvert.SerializeObject(pobj_BodyObject == null ? "" : pobj_BodyObject));
lobj_Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
lobj_HTTPResponse = await iobj_HTTPClient.PostAsync(ps_URI, lobj_Content);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Exception lobj_Exception = new Exception(lobj_HTTPResponse.ToString());
lobj_Exception.Source = "HTTPGetWebService for: " + ps_URI;
App.ProcessException(lobj_Exception);
}
else
{
lb_Response = true;
}
}
catch (Exception ex)
{
App.ProcessException(ex);
}
finally
{
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
//Debug.WriteLine("WebAPICaller-CallWebService-1: Done");
}
return lb_Response;
}
private async Task<HttpClient> GetClient()
{
HttpClient lobj_HTTPClient = null;
if (lobj_HTTPClient == null)
{
lobj_HTTPClient = new HttpClient();
lobj_HTTPClient.DefaultRequestHeaders.Add("Accept", "application/json");
lobj_HTTPClient.MaxResponseContentBufferSize = 2147483647;
lobj_HTTPClient.Timeout = new TimeSpan(0,0,0,0,60000);
}
return lobj_HTTPClient;
}
}
}
Sorry I forget to include the method in the CountryStateCity view model that calls the webapi helper class.
public async Task<bool> LoadData()
{
string ls_Response = "";
string ls_WorkURI = "";
WebAPICaller lobj_WebAPICaller = null;
bool lb_DataLoaded = false;
try
{
IsDataLoaded = false;
//Debug.WriteLine("City Data Load");
lobj_WebAPICaller = new WebAPICaller();
ls_WorkURI = ic_CoutryStateCityAPIUrl.Replace("{Language}", "EN");
ls_Response = await lobj_WebAPICaller.HTTPGetWebServiceAsync(ls_WorkURI);
if (ls_Response.Trim().Length == 0)
{
AddErrorEntry();
}
else
{
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>();
StateItems_ForList = new ObservableCollection<GBSState_ForList>();
CityItems_ForList = new ObservableCollection<GBSCity_ForList>();
iobj_CountryStateCity = JsonConvert.DeserializeObject<ObservableCollection<GBSCountryStateCity>>(ls_Response);
//Now load the display lists
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>(
(from lobj_Country in iobj_CountryStateCity
select new GBSCountry_ForList()
{
ID = lobj_Country.Country_Code,
Value = lobj_Country.Country_Name_Text
}).Distinct().ToList());
CountryItems_ForList.Insert(0, new GBSCountry_ForList
{
ID = "XX",
Value = "Base Value"
});
lb_DataLoaded = true;
}
}
catch (Exception ex)
{
AddErrorEntry();
App.ProcessException(ex);
}
finally
{
IsDataLoaded = true;
if (lobj_WebAPICaller != null)
lobj_WebAPICaller.Dispose();
}
return lb_DataLoaded;
}
So after much time, I believe I figured out what the problem is. The problem started to manifest itself again with smaller amounts of data and I could not figure out why. The problem appeared. The issue appears to be the IP address I was using. (I was using the IP address of the actual laptop I was hosting both the App and WebAPIs on.) It appears you have to use one of the other network adaptors for the emulator to have this work reliably.
Here are the steps I used to resolved this:
I launched my Windows 10 mobile emulator.
Click on the >> (Tools) icon in the tool bar of the emulator.
Click on the Network tab of the Additional Tools window.
Look in the list for the network adaptor labeled Desktop Adaptor #1 and copy the IP address.
Edit the Applicationhost.config file in the folder of the WebAPI project.
Find the entry in the file for site name="XXXXX" where XXXXX is the name of the Visual Studio project you are hosting your WebAPIs in.
Within the section of the entry for your WebAPI project, add a binding for the IP address you copied from in step 4. It should look something like this:
<binding protocol="http" bindingInformation="*:56952:169.254.69.220" />
Where 56952 is the port my IIS Express is hosting the WebAPIs on and 169.254.69.220 is the IP address I copied from step 4. After adding this, I was able to connect to locally hosted WebAPIs in IIS Express.
Hope this helps.
The Custom Pipeline component developed reads the incoming stream to a folder and pass only some meta data through the MessageBox.I am using the one already availaible in Code Project
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using System.IO;
namespace SendLargeFilesDecoder
{
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_Decoder)]
[System.Runtime.InteropServices.Guid("53fd04d5-8337-42c2-99eb-32ac96d1105a")]
public class SendLargeFileDecoder : IBaseComponent,
IComponentUI,
IComponent,
IPersistPropertyBag
{
#region IBaseComponent
private const string _description = "Pipeline component used to save large files to disk";
private const string _name = "SendLargeFileDecoded";
private const string _version = "1.0.0.0";
public string Description
{
get { return _description; }
}
public string Name
{
get { return _name; }
}
public string Version
{
get { return _version; }
}
#endregion
#region IComponentUI
private IntPtr _icon = new IntPtr();
public IntPtr Icon
{
get { return _icon; }
}
public System.Collections.IEnumerator Validate(object projectSystem)
{
return null;
}
#endregion
#region IComponent
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
if (_largeFileLocation == null || _largeFileLocation.Length == 0)
_largeFileLocation = Path.GetTempPath();
if (_thresholdSize == null || _thresholdSize == 0)
_thresholdSize = 4096;
if (pInMsg.BodyPart.GetOriginalDataStream().Length > _thresholdSize)
{
Stream originalStream = pInMsg.BodyPart.GetOriginalDataStream();
string srcFileName = pInMsg.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties").ToString();
string largeFilePath = _largeFileLocation + System.IO.Path.GetFileName(srcFileName);
FileStream fs = new FileStream(largeFilePath, FileMode.Create);
// Write message to disk
byte[] buffer = new byte[1];
int bytesRead = originalStream.Read(buffer, 0, buffer.Length);
while (bytesRead != 0)
{
fs.Flush();
fs.Write(buffer, 0, buffer.Length);
bytesRead = originalStream.Read(buffer, 0, buffer.Length);
}
fs.Flush();
fs.Close();
// Create a small xml file
string xmlInfo = "<MsgInfo xmlns='http://SendLargeFiles'><LargeFilePath>" + largeFilePath + "</LargeFilePath></MsgInfo>";
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(xmlInfo);
MemoryStream ms = new MemoryStream(byteArray);
pInMsg.BodyPart.Data = ms;
}
return pInMsg;
}
#endregion
#region IPersistPropertyBag
private string _largeFileLocation;
private int _thresholdSize;
public string LargeFileLocation
{
get { return _largeFileLocation; }
set { _largeFileLocation = value; }
}
public int ThresholdSize
{
get { return _thresholdSize; }
set { _thresholdSize = value; }
}
public void GetClassID(out Guid classID)
{
classID = new Guid("CA47347C-010C-4B21-BFCB-22F153FA141F");
}
public void InitNew()
{
}
public void Load(IPropertyBag propertyBag, int errorLog)
{
object val1 = null;
object val2 = null;
try
{
propertyBag.Read("LargeFileLocation", out val1, 0);
propertyBag.Read("ThresholdSize", out val2, 0);
}
catch (ArgumentException)
{
}
catch (Exception ex)
{
throw new ApplicationException("Error reading PropertyBag: " + ex.Message);
}
if (val1 != null)
_largeFileLocation = (string)val1;
if (val2 != null)
_thresholdSize = (int)val2;
}
public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
{
object val1 = (object)_largeFileLocation;
propertyBag.Write("LargeFileLocation", ref val1);
object val2 = (object)_thresholdSize;
propertyBag.Write("ThresholdSize", ref val2);
}
#endregion
}
}
The issue here is the LargeFileLocation is configurable in the receive pipeline. If I give a location for the first time for example E:\ABC\ the files are sent to the location.
But if I change the location to E:\DEF\ the files are still being sent to the previous location E:\ABC. I tried to create a new biztalk application deleting the old one but still I get the files dropped in to the old location E:\ABC\ not sure why.
Most likely the issue is with respect to Property definition of LargeFileLocation and its implementation and usage in IPersistPropertyBag interfaces. You can try following things:
Check if you have added E:\ABC path in Pipeline at design time. If
yes remove it from there and set in Admin console for first time
also and see how it behaves, my feeling is it will take temp path
location.
Change the Properties and IPersistPropertyBag implementation to use property with declaration such as public string LargeFileName {get;set;} i.e. no local variables _largeFileName.
Have you deleted the dll in %BizTalkFolder%\Pipeline Components\ ?
To refresh the pipeline component, you need delete the old dll file/remove the item in VS toolbox. then restart the VS, deploy it again.
and for this LargeFileLocation , I suggest you make it as a property so you can config it.
I am trying to write a REST web service using ServiceStack that accepts variable paths off of route. For example:
[Route("/{group}"]
public class Entity : IReturn<SomeType> {}
This throws a NotSupported Exception "RestPath '/{collection}' on type Entity is not supported". However, if I change the path as follows (along with the associated path in AppHost configuration) to:
[Route("/rest/{group}"]
It works just fine. In order to integrate with the system that I am working with, I need to use /{group}.
ServiceStack now allows you to add a fallback route from the / root path to handle any un-matched requests, that's not handled by a catch-all handler or refers to an existing static file. So in v3.9.56 you can now do:
[FallbackRoute("/{group}"]
public class Entity : IReturn<SomeType> {}
An alternative option is to register a IAppHost.CatchAllHandlers to handle un-matched routes, but you would need to return your own IHttpHandler to handle the request or alternatively return a RedirectHttpHandler to redirect to a different route that is managed by ServiceStack.
My current work in progress, a plugin to serve the default page to all 'not found' routes without changing the url in the browser, includes most of what you'll need to handle a global wildcard route. Use it to get you started.
To understand what this code is doing, it helps to understand ServiceStack's routing priority, and how CatchAllHandlers fit into the process. ServiceStack calls ServiceStackHttpHandlerFactory.GetHandler to get the handler for the current route.
ServiceStackHttpHandlerFactory.GetHandler returns:
A matching RawHttpHandler, if any.
If the domain root, the handler returned by GetCatchAllHandlerIfAny(...), if any.
If the route matches a metadata uri (I'm skipping over the exact logic here, as it's not important for your question), the relevant handler, if any.
The handler returned by ServiceStackHttpHandlerFactory.GetHandlerForPathInfo if any.
NotFoundHandler.
ServiceStackHttpHandlerFactory.GetHandlerForPathInfo returns:
If the url matches a valid REST route, a new RestHandler.
If the url matches an existing file or directory, it returns
the handler returned by GetCatchAllHandlerIfAny(...), if any.
If it's a supported filetype, a StaticFileHandler,
If it's not a supported filetype, the ForbiddenHttpHandler.
The handler returned by GetCatchAllHandlerIfAny(...), if any.
null.
The CatchAllHandlers array contains functions that evaluate the url and either return a handler, or null. The functions in the array are called in sequence and the first one that doesn't return null handles the route. Let me highlight some key elements:
First, the plugin adds a CatchAllHandler to the appHost.CatchAllHandlers array when registered.
public void Register(IAppHost appHost)
{
appHost.CatchAllHandlers.Add((string method, string pathInfo, string filepath) =>
Factory(method, pathInfo, filepath));
}
Second, the CatchAllHandler. As described above, the function may be called for the domain root, an existing file or directory, or any other unmatched route. Your method should return a handler, if your criteria are met, or return null.
private static Html5ModeFeature Factory(String method, String pathInfo, String filepath)
{
var Html5ModeHandler = Html5ModeFeature.Instance;
List<string> WebHostRootFileNames = RootFiles();
// handle domain root
if (string.IsNullOrEmpty(pathInfo) || pathInfo == "/")
{
return Html5ModeHandler;
}
// don't handle 'mode' urls
var mode = EndpointHost.Config.ServiceStackHandlerFactoryPath;
if (mode != null && pathInfo.EndsWith(mode))
{
return null;
}
var pathParts = pathInfo.TrimStart('/').Split('/');
var existingFile = pathParts[0].ToLower();
var catchAllHandler = new Object();
if (WebHostRootFileNames.Contains(existingFile))
{
var fileExt = Path.GetExtension(filepath);
var isFileRequest = !string.IsNullOrEmpty(fileExt);
// don't handle directories or files that have another handler
catchAllHandler = GetCatchAllHandlerIfAny(method, pathInfo, filepath);
if (catchAllHandler != null) return null;
// don't handle existing files under any event
return isFileRequest ? null : Html5ModeHandler;
}
// don't handle non-physical urls that have another handler
catchAllHandler = GetCatchAllHandlerIfAny(method, pathInfo, filepath);
if (catchAllHandler != null) return null;
// handle anything else
return Html5ModeHandler;
}
In the case of the wildcard at the root domain, you may not want to hijack routes that can be handled by another CatchAllHandler. If so, to avoid infinite recursion, you'll need a custom GetCatchAllHandlerIfAny method.
//
// local copy of ServiceStackHttpHandlerFactory.GetCatchAllHandlerIfAny, prevents infinite recursion
//
private static IHttpHandler GetCatchAllHandlerIfAny(string httpMethod, string pathInfo, string filePath)
{
if (EndpointHost.CatchAllHandlers != null)
{
foreach (var httpHandlerResolver in EndpointHost.CatchAllHandlers)
{
if (httpHandlerResolver == Html5ModeFeature.Factory) continue; // avoid infinite recursion
var httpHandler = httpHandlerResolver(httpMethod, pathInfo, filePath);
if (httpHandler != null)
return httpHandler;
}
}
return null;
}
Here's the complete, and completely untested, plugin. It compiles. It carries no warranty of fitness for any specific purpose.
using ServiceStack;
using ServiceStack.Common.Web;
using ServiceStack.Razor;
using ServiceStack.ServiceHost;
using ServiceStack.Text;
using ServiceStack.WebHost.Endpoints;
using ServiceStack.WebHost.Endpoints.Formats;
using ServiceStack.WebHost.Endpoints.Support;
using ServiceStack.WebHost.Endpoints.Support.Markdown;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
namespace MyProject.Support
{
public enum DefaultFileFormat
{
Markdown,
Razor,
Static
}
public class Html5ModeFeature : EndpointHandlerBase, IPlugin
{
private FileInfo fi { get; set; }
private DefaultFileFormat FileFormat { get; set; }
private DateTime FileModified { get; set; }
private byte[] FileContents { get; set; }
public MarkdownHandler Markdown { get; set; }
public RazorHandler Razor { get; set; }
public object Model { get; set; }
private static Dictionary<string, string> allDirs;
public string PathInfo { get; set; }
public void Register(IAppHost appHost)
{
appHost.CatchAllHandlers.Add((string method, string pathInfo, string filepath) =>
Factory(method, pathInfo, filepath));
}
private Html5ModeFeature()
{
foreach (var defaultDoc in EndpointHost.Config.DefaultDocuments)
{
if (PathInfo == null)
{
var defaultFileName = Path.Combine(Directory.GetCurrentDirectory(), defaultDoc);
if (!File.Exists(defaultFileName)) continue;
PathInfo = (String)defaultDoc; // use first default document found.
}
}
SetFile();
}
private static Html5ModeFeature instance;
public static Html5ModeFeature Instance
{
get { return instance ?? (instance = new Html5ModeFeature()); }
}
public void SetFile()
{
if (PathInfo.EndsWith(MarkdownFormat.MarkdownExt) || PathInfo.EndsWith(MarkdownFormat.TemplateExt))
{
Markdown = new MarkdownHandler(PathInfo);
FileFormat = DefaultFileFormat.Markdown;
return;
}
if (PathInfo.EndsWith(Razor.RazorFormat.RazorFileExtension)) {
Razor = new RazorHandler(PathInfo);
FileFormat = DefaultFileFormat.Razor;
return;
}
FileContents = File.ReadAllBytes(PathInfo);
FileModified = File.GetLastWriteTime(PathInfo);
FileFormat = DefaultFileFormat.Static;
}
//
// ignore request.PathInfo, return default page, extracted from StaticFileHandler.ProcessResponse
//
public void ProcessStaticPage(IHttpRequest request, IHttpResponse response, string operationName)
{
response.EndHttpHandlerRequest(skipClose: true, afterBody: r =>
{
TimeSpan maxAge;
if (r.ContentType != null && EndpointHost.Config.AddMaxAgeForStaticMimeTypes.TryGetValue(r.ContentType, out maxAge))
{
r.AddHeader(HttpHeaders.CacheControl, "max-age=" + maxAge.TotalSeconds);
}
if (request.HasNotModifiedSince(fi.LastWriteTime))
{
r.ContentType = MimeTypes.GetMimeType(PathInfo);
r.StatusCode = 304;
return;
}
try
{
r.AddHeaderLastModified(fi.LastWriteTime);
r.ContentType = MimeTypes.GetMimeType(PathInfo);
if (fi.LastWriteTime > this.FileModified)
SetFile(); //reload
r.OutputStream.Write(this.FileContents, 0, this.FileContents.Length);
r.Close();
return;
}
catch (Exception ex)
{
throw new HttpException(403, "Forbidden.");
}
});
}
private void ProcessServerError(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
{
var sb = new StringBuilder();
sb.AppendLine("{");
sb.AppendLine("\"ResponseStatus\":{");
sb.AppendFormat(" \"ErrorCode\":{0},\n", 500);
sb.AppendFormat(" \"Message\": HTML5ModeHandler could not serve file {0}.\n", PathInfo.EncodeJson());
sb.AppendLine("}");
sb.AppendLine("}");
httpRes.EndHttpHandlerRequest(skipClose: true, afterBody: r =>
{
r.StatusCode = 500;
r.ContentType = ContentType.Json;
var sbBytes = sb.ToString().ToUtf8Bytes();
r.OutputStream.Write(sbBytes, 0, sbBytes.Length);
r.Close();
});
return;
}
private static List<string> RootFiles()
{
var WebHostPhysicalPath = EndpointHost.Config.WebHostPhysicalPath;
List<string> WebHostRootFileNames = new List<string>();
foreach (var filePath in Directory.GetFiles(WebHostPhysicalPath))
{
var fileNameLower = Path.GetFileName(filePath).ToLower();
WebHostRootFileNames.Add(Path.GetFileName(fileNameLower));
}
foreach (var dirName in Directory.GetDirectories(WebHostPhysicalPath))
{
var dirNameLower = Path.GetFileName(dirName).ToLower();
WebHostRootFileNames.Add(Path.GetFileName(dirNameLower));
}
return WebHostRootFileNames;
}
private static Html5ModeFeature Factory(String method, String pathInfo, String filepath)
{
var Html5ModeHandler = Html5ModeFeature.Instance;
List<string> WebHostRootFileNames = RootFiles();
// handle domain root
if (string.IsNullOrEmpty(pathInfo) || pathInfo == "/")
{
return Html5ModeHandler;
}
// don't handle 'mode' urls
var mode = EndpointHost.Config.ServiceStackHandlerFactoryPath;
if (mode != null && pathInfo.EndsWith(mode))
{
return null;
}
var pathParts = pathInfo.TrimStart('/').Split('/');
var existingFile = pathParts[0].ToLower();
var catchAllHandler = new Object();
if (WebHostRootFileNames.Contains(existingFile))
{
var fileExt = Path.GetExtension(filepath);
var isFileRequest = !string.IsNullOrEmpty(fileExt);
// don't handle directories or files that have another handler
catchAllHandler = GetCatchAllHandlerIfAny(method, pathInfo, filepath);
if (catchAllHandler != null) return null;
// don't handle existing files under any event
return isFileRequest ? null : Html5ModeHandler;
}
// don't handle non-physical urls that have another handler
catchAllHandler = GetCatchAllHandlerIfAny(method, pathInfo, filepath);
if (catchAllHandler != null) return null;
// handle anything else
return Html5ModeHandler;
}
//
// Local copy of private StaticFileHandler.DirectoryExists
//
public static bool DirectoryExists(string dirPath, string appFilePath)
{
if (dirPath == null) return false;
try
{
if (!ServiceStack.Text.Env.IsMono)
return Directory.Exists(dirPath);
}
catch
{
return false;
}
if (allDirs == null)
allDirs = CreateDirIndex(appFilePath);
var foundDir = allDirs.ContainsKey(dirPath.ToLower());
//log.DebugFormat("Found dirPath {0} in Mono: ", dirPath, foundDir);
return foundDir;
}
//
// Local copy of private StaticFileHandler.CreateDirIndex
//
static Dictionary<string, string> CreateDirIndex(string appFilePath)
{
var indexDirs = new Dictionary<string, string>();
foreach (var dir in GetDirs(appFilePath))
{
indexDirs[dir.ToLower()] = dir;
}
return indexDirs;
}
//
// Local copy of private StaticFileHandler.GetDirs
//
static List<string> GetDirs(string path)
{
var queue = new Queue<string>();
queue.Enqueue(path);
var results = new List<string>();
while (queue.Count > 0)
{
path = queue.Dequeue();
try
{
foreach (string subDir in Directory.GetDirectories(path))
{
queue.Enqueue(subDir);
results.Add(subDir);
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
}
return results;
}
//
// local copy of ServiceStackHttpHandlerFactory.GetCatchAllHandlerIfAny, prevents infinite recursion
//
private static IHttpHandler GetCatchAllHandlerIfAny(string httpMethod, string pathInfo, string filePath)
{
if (EndpointHost.CatchAllHandlers != null)
{
foreach (var httpHandlerResolver in EndpointHost.CatchAllHandlers)
{
if (httpHandlerResolver == Html5ModeFeature.Factory) continue; // avoid infinite recursion
var httpHandler = httpHandlerResolver(httpMethod, pathInfo, filePath);
if (httpHandler != null)
return httpHandler;
}
}
return null;
}
public override void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
{
switch (FileFormat)
{
case DefaultFileFormat.Markdown:
{
Markdown.ProcessRequest(httpReq, httpRes, operationName);
break;
}
case DefaultFileFormat.Razor:
{
Razor.ProcessRequest(httpReq, httpRes, operationName);
break;
}
case DefaultFileFormat.Static:
{
fi.Refresh();
if (fi.Exists) ProcessStaticPage(httpReq, httpRes, operationName); else ProcessServerError(httpReq, httpRes, operationName);
break;
}
default:
{
ProcessServerError(httpReq, httpRes, operationName);
break;
}
}
}
public override object CreateRequest(IHttpRequest request, string operationName)
{
return null;
}
public override object GetResponse(IHttpRequest httpReq, IHttpResponse httpRes, object request)
{
return null;
}
}
}
I have a basic WPF application where the client writes to a database. I'm using IIS on a server 2012 machine to host the web service. I'm trying to implement Forms authentication, and I have all that working (passing a username and password in the xaml.cs from the client which authenticates my ASP.NET user which works. I then want to implement ASP.NET roles authorization for different commands (Submit Request, Remove Request, etc). The method we're supposed to use is "[PrincipalPermission(SecurityAction.Demand, Role = "Allowed")]"
In theory this should just use the credentials passed in the client (which I've confirmed works) when I try to hit the buttons and it should check if the user I passed is in the role, and if so it allows and if not it denies. However, whether or not the user is in the role it still says "Access is Denied".
Any thoughts?
using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Security.Permissions;
using RequestRepository;
using System.Threading;
using System.Web;
namespace RequestServiceLibrary
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RequestService : IRequestService
{
private List<Request> requests = new List<Request>();
private RequestLibraryEntities context = new RequestLibraryEntities();
[PrincipalPermission(SecurityAction.Demand, Role = "Allowed")]
public string SubmitRequest(Request req)
{
Thread.CurrentPrincipal = HttpContext.Current.User;
if (context.Requests.Count() == 0)
populateRequests();
req.Id = Guid.NewGuid().ToString();
req.TimeSubmitted = DateTime.Now;
requests.Add(req);
addRequest(req);
return req.Id;
}
[PrincipalPermission(SecurityAction.Demand, Role = "Allowed")]
public bool UpdateRequest(Request req)
{
Thread.CurrentPrincipal = HttpContext.Current.User;
bool returnval = false;
try
{
var getobject = requests.Find(x => x.Id.Equals(req.Id));
if (getobject != null) //checks to make sure the object isn't empty
{
getobject.Username = req.Username;
getobject.Password = req.Password;
getobject.RequestedResource = req.RequestedResource;
getobject.TimeSubmitted = req.TimeSubmitted;
}
//Find the request object in the database
var Id = Guid.Parse(req.Id);
var rl = context.Requests.Find(Id);
//Update that object with the values from req
rl.Username = req.Username;
rl.Password = req.Password;
rl.RequestedResource = req.RequestedResource;
rl.TimeTransmitted = req.TimeSubmitted;
context.SaveChanges();
returnval = true;
return returnval;
}
catch (Exception) { return returnval; }
}
public List<Request> GetRequests()
{
populateRequests();
return requests;
}
[PrincipalPermission(SecurityAction.Demand, Role = "Disallowed")]
public bool RemoveRequest(string id)
{
bool rval = false;
try
{
Request req = requests.Find(x => x.Id.Equals(id));
requests.Remove(req);
rval = delRequest(req);
return rval;
}
catch (Exception)
{
return false;
}
}
private void populateRequests()
{
requests = new List<Request>();
var rl = context.Requests.ToList();
foreach (var r in rl)
{
requests.Add(new Request()
{
Id = r.Id.ToString(),
Password = r.Password,
RequestedResource = r.RequestedResource,
TimeSubmitted = r.TimeTransmitted,
Username = r.Username
});
}
}
private void addRequest(Request req)
{
try
{
var r = context.Requests.Create();
r.Id = Guid.Parse(req.Id);
r.Username = req.Username;
r.Password = req.Password;
r.RequestedResource = req.RequestedResource;
r.TimeTransmitted = req.TimeSubmitted;
context.Requests.Add(r);
context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Console.WriteLine("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
}
private bool delRequest(Request req)
{
Guid Id = Guid.Parse(req.Id);
var r = context.Requests.Create();
r.Id = Id;
var rl = context.Requests.Find(Id);
try
{
context.Requests.Remove(rl);
context.SaveChanges();
return true;
}
catch (Exception) { return false; }
}
}
}
In order to be able to use PrincipalPermissionAttribute in this way, you need to first set Thread.CurrentPrincipal to a Principal with the appropriate roles ("Allowed" in this case).
For example you could use ClientRoleProvider to do this, or simply create the Principal manually (possibly using roles you retrieve from the web service).