Host web UI with executable application? - web-services

This may be a very stupid question but I have spent nearly 5 hours doing research on the web and found nothing to fully clarify my doubts.
In few words I have been asked for a possible employer to develop certain executable application as part of a "Technical Test". Supposedly they're measuring my expertise working with WCF. I was given two days to develop such App and all the information about it is the following:
Deliverable:
                - An executable that
                                * When APP is ran, it should host a WCF service (SERVICE) as well as a
web UI (UI) accessible by web browsers.
                                * Through the UI, user should be able to add or delete messages stored in a
database (DB).
                                * The UI should also display the current list of messages stored in the DB.
                                * If changes are made to the DB, those changes should show up in the UI
without the need to reload the page.
                - All of the project source code.
               
Additional notes:
Use of existing libraries is allowed as long as they are clearly referenced
Now, I understand that you can host a WCF Web Service using a Console Application (among other options) and the Service will be alive as long as the application is running. I also know that any Web Application can access this service by just adding a Service Reference, creating a client of its type and calling its methods. My confusion begins when they ask me to put all together in one executable application:
When APP is ran, it should host a WCF service (SERVICE) as well as a web UI (UI) accessible by web browsers.
What is that supposed to mean?? How can I host a Web UI using an executable?? Am I supposed to develop something like IIS and at the same time somehow define the html and server side code on the APP?
I did some research and I found a class(HttpListener) that allows you to open an http port, listen and then send back some html thru it. A very simple class. If this is a solution I can't see how to implement it. Other than that I couldn't find anything else on the web.
I would appreciate any opinion on the matter, even if I'm not able to develop the solution in time I would like to know how to do it. And if I'm missing some important basic concept regarding WCF or Web Hosting please I would greatly appreciate some clarification. Thanks in advance.

You can use OWIN to "self host" web apps.
An overview and further information can be found here - http://codeopinion.com/self-host-asp-net-web-api/

I solved the problem by hosting the web UI in the service itself. A service operation can return anything, even a Stream of bytes with the html for the browser to render. Here's the code.
[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Stream GetUserInterface()
{
var appDirectoryName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var htmlFilePath = appDirectoryName + "\\UI.html";
var buffer = File.ReadAllBytes(htmlFilePath);
if (WebOperationContext.Current != null)
WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
return new MemoryStream(buffer);
}
As you can see on the same directory than the executable I placed a UI.html file, this file contains all my UI html, javascript and css. Then I convert it to an array of bytes and return that to the browser.
So the only thing I have to do to access the UI is run the application and then browse to this operation. Eg: http://localhost:8080/MyService/GetUserInterface.
For the database part I used SQlite, in this way the application became a standalone that can be installed in a PC and run immediately without the need of Database or Web hosting. Exactly what the test requested.
Alternatively the class that I mentioned in my question (HttpListener) can also be used to host the Web UI instead of the service. This is another solution.
private static void HostUI()
{
while (true)
{
using (var listener = new HttpListener())
{
listener.Prefixes.Add("http://localhost:7070/");
listener.Start();
var context = listener.GetContext();
var response = context.Response;
//The .html file will be in the same folder where the .exe is
var appDirectoryName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var htmlFilePath = appDirectoryName + "\\UI.html";
var buffer = File.ReadAllBytes(htmlFilePath);
response.ContentType = "text/html";
response.ContentLength64 = buffer.Length;
Console.WriteLine(buffer.Length);
var output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
listener.Close();
}
}
}
The reason why I used an infinite loop is because the HttpListener class implementation processes only one order by loop, so in order to able to request the UI multiple times you need to do this.
Then you can browse to http://localhost:7070/ and you'll see the UI too.
You can put this code in an independent thread to host the Web UI without affecting the main thread.

Related

SOAP request going to localhost

I'm testing a SOAP web service using Soap UI.
The WSDL location is on an external server, so I load and create a new project like this:
However, when I try to create a request, the endpoint points to localhost:
I have to manually insert the right endpoint URL for the request to work.
What could be behind this?
If you open the wsdl file in a browser or in a text editor, it will be clear that the endpoint has the same url, i.e., localhost, but not the actual one. That is the reason you are experiencing that.
This mostly happens also as developers try to use localhost while developing and it is not required/no point/does not make sense to have their hostname in wsdl unless it is a public wsdl. If QA team is using, then they will set their hostname in the endpoint, similarly for the other environments. So, you really do not have to worry about it, I believe.
In order to set the right endpoint at one place and apply for all requests( instead of changing it for each and every request), do the following:
Go to the service interface, right click and show interface viewer.
Click on Service Endpoints tab.
Click on + button.
Add the actual endpoint what you wanted
Click on Assign button, and select All requests and Test Requests from dropdown and say ok
Repeat the same if you have multiple interfaces in your project.
Save the project, and you are done.
You should be able to see the desired endpoint for all the existing test requests and even for the new one that you are going to create later.

From Classic ASP consume a ssl .net web service

I've researched the web extensively before posting this questions here, couldn't find anything usefull for me, so here it goes. Sorry in advance for the wall of text.
I have a classic ASP website that needs to call a web service method (not wcf, but an asmx page) on a asp.net website (4.0)
The way I consume the web service is as follows:
I have a webserviceclass.asp that helps me consume web services in classic asp.
code of webserviceclass.asp -> https://docs.google.com/file/d/0B1Wr1Kw74xy3ZkducEtiTWNtWDA/edit?usp=sharing
Then calling the class to consume the method.
Dim strWebServiceResult
Dim ws
Set ws = new webservice
ws.url = "http://www.myurl.com/mywebservice.asmx"
ws.method = "HelloWorld"
ws.parameters.Add "Parameter1", "Test1"
ws.parameters.Add "Parameter2", "Test2"
ws.Execute
strWebServiceResult = ws.response
This works perfectly if www.myurl.com is not encrypted, strWebServiceResult holds the XML returned by mywebservice.asmx, however I really need the data traveling between the users and www.myurl.com to be encrypted (mywebservice.asmx has parameters for login and password to authenticate on the webservice) so I bought an SSL cert and assigned it to www.myurl.com website becoming https://www.myurl.com
So when I set the URL in the class to: ws.url = "https://www.myurl.com/mywebservice.asmx"
and that is the only thing that changes, the method doesn't get called anymore. I have log on pageinit and pageload of the mywebservice.aspx and it doesn't even get called.
Sorry about the long text, and I hope someone can help me. I have no experience with jquery or json and I'd rather not go that way to solve this problem, but if that's the only way, I can try if there's enough info out there to help me out but I'd rather try to fix my current code if that possible. I just don't understand why it works with http but not with https
Many thanks in advance
In this SO question ("Can't use HTTPS with ServerXMLHTTP object"), this issue was addressed.
For a starter, you should be using "MSXML2.ServerXMLHTTP" instead of "Microsoft.XMLHTTP" on the server side. This is discussed here, f.i.: differences between Msxml2.ServerXMLHTTP and WinHttp.WinHttpRequest?

Am I reinventing the wheel with this ASP.NET Web API based webserver?

In a standalone (selfhosted) application, I would like to have an httpserver that on a single base adress can either serve simple web pages (without any serverside dynamics/scripting, it just returns the content request files) or serve RESTful webservices:
when http://localhost:8070/{filePath} is requested, it should return the content of the file (html, javascript, css, images), just like a normal simple webserver
everything behind http://localhost:8070/api/ should just act as a normal RRESTful Web API
My current approach uses ASP.NET Web API to server both the html pages and the REST APIs:
var config = new HttpSelfHostConfiguration("http://localhost:8070/");
config.Formatters.Add(new WebFormatter());
config.Routes.MapHttpRoute(
name: "Default Web",
routeTemplate: "{fileName}",
defaults: new { controller = "web", fileName = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Default API",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
The WebController is the controller that serves the web pages with this naive code:
public class WebController : ApiController
{
public HttpResponseMessage Get(string fileName = null)
{
/// ...
var filePath = Path.Combine(wwwRoot, fileName);
if (File.Exists(filePath))
{
if (HasCssExtension(filePath))
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
GetFileContent(filePath),
"text/css");
}
if (HasJavaScriptExtension(filePath))
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
GetFileContent(filePath),
"application/javascript");
}
return this.Request.CreateResponse(
HttpStatusCode.OK,
GetFileContent(filePath),
"text/html");
}
return this.Request.CreateResponse(
HttpStatusCode.NotFound,
this.GetFileContnet(Path.Combine(wwwRoot, "404.html")),
"text/html");
}
}
And again, for everything behind /api, controllers for normal REST APIs are used.
My question now is: Am I on the right track? I kind of feel that I am rebuilding a webserver here, reinventing the wheel. And I guess that there are probably a lot of http request web browsers could make that I do not handle correctly here.
But what other option do I have if I want to self host and at the same time server REST web APIs and web pages over the same base address?
Looks like you are trying to recreate asp.net FileHandler for self host. There is a better solution though. Using Katana(an OWIN host) as the hosting layer for web API. OWIN supports hosting multiple OWIN frameworks in the same app. In your case, you can host both web API and a file handler in the same OWIN app.
Filip has a good blog post on this to get you started here. You can use configuration code like this,
public void Configuration(IAppBuilder appBuilder)
{
// configure your web api.
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default-api", "api/{controller}");
appBuilder.UseWebApi(config);
// configure your static file handler.
appBuilder.UseStaticFiles();
}
IMO there is nothing wrong with what you are doing. I use self-host for delivering files, html docs as well as being a regular API. At the core, self-host is using HTTP.SYS just as IIS is.
As RaghuRam mentioned there are Owin hosts that have some optimizations for serving static files, but WCF selfhost is quite capable of getting decent perf for serving files.
See this link which uses a more straigftforward approach
Setting app a separate Web API project and ASP.NET app
RouteTable.Routes.IgnoreRoute(".js");
RouteTable.Routes.IgnoreRoute(".html");

How to use Intersystem's session within web services

I am trying to set some session information in one web method and return it in another web method, but the session data is always empty on the second web method. Here is what I tried
Web Method 1 - Sets session information
Method StartSession() As %String [WebMethod]
{
set %session.NewSession = 1
set %session.Data("key") = "dude"
Quit "Session Started"
}
Web Method 2 - Gets session information should return dude, but is returning blank
Method TestSession() As %String [WebMethod]
{
Quit $Get(%session.Data("key"))
}
To use sessions with Cache web services you need to set the SOAPSESSION class parameter of your web service class equal to 1.
Doing so will cause the web service to return a SOAP session header in the response. If you are using a client that was built to expect this header you may not need to set up anything else. Otherwise, your client application will have to read this header and include it in all further requests, so the server can know which session you are using. An example of this header given in the documentation is:
<csp:CSPCHD xmlns:csp="http://www.intersystems.com/SOAPheaders">value of
CPSCHD token</csp:CSPCHD>
Note that security is a separate issue that your example doesn't address.
Also note that Intersystems has decided that web services will continue to use a license for some period of time after the call has been made. I can't find documentation on this, and I believe it's something like a few seconds per call. I believe that this can cause license issues that would not occur if you used other software to provide web services, and had that other software call Cache via some mechanism other than web services. I believe this is true even when that other software carefully follows all the rules in the license agreement about named and anonymous users. However, I'm not certain about any of this licensing stuff. Still, you might want to do some testing before you commit to an architecture.
As an alternative to psr's answer another way to handle state is to use custom SOAP headers.
For example:
Create a class for your custom SOAP headers like below:
Class Headers.TimeStamp Extends %SOAP.Header
{
Property TimeSent As %TimeStamp;
}
In the web method do this:
set h=##class(Headers.TimeStamp).%New()
set h.TimeSent=$ZTIMESTAMP
do ..HeadersOut.SetAt(h,"Timestamp")
This will generate the following SOAP header:
<SOAP-ENV:Header>
<TimeStamp xmlns:hdr="http://www.myapp.org">
<TimeSent>60712,70996.027Z</TimeSent>
</TimeStamp>
</SOAP-ENV:Header>
This will allow state to be maintained within the SOAP headers rather than using Cache's session management.

Login failed when a web service tries to communicate with SharePoint 2007

I created a very simple webservice in ASP.NET 2.0 to query a list in SharePoint 2007 like this:
namespace WebService1
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public string ShowSPMyList()
{
string username = this.User.Identity.Name;
return GetList();
}
private string GetList()
{
string resutl = "";
SPSite siteCollection = new SPSite("http://localhost:89");
using (SPWeb web = siteCollection.OpenWeb())
{
SPList mylist = web.Lists["MySPList"];
SPQuery query = new SPQuery();
query.Query = "<Where><Eq><FieldRef Name=\"AssignedTo\"/><Value Type=\"Text\">Ramprasad</Value></Eq></Where>";
SPListItemCollection items = mylist.GetItems(query);
foreach (SPListItem item in items)
{
resutl = resutl + SPEncode.HtmlEncode(item["Title"].ToString());
}
}
return resutl;
}
}
}
This web service runs well when tested using the built-in server of Visual Studio 2008. The username indicates exactly my domain account (domain\myusername).
However when I create a virtual folder to host and launch this web service (still located in the same machine with SP2007), I got the following error when invoking ShowSPMyList() method, at the line to execute OpenWeb(). These are the details of the error:
System.Data.SqlClient.SqlException: Cannot open database "WSS_Content_8887ac57951146a290ca134778ddc3f8" requested by the login. The login failed.
Login failed for user 'NT AUTHORITY\NETWORK SERVICE'.
Does anyone have any idea why this error happens? Why does the web service run fine inside Visual Studio 2008, but not when running stand-alone? I checked and in both cases, the username variable has the same value (domain\myusername).
Thank you very much.
Thank you very much for the replies. I'll look into the documents to see how i can change the settings related to the application pool as suggested.
I want to make clear that i wanted to build a webservice to run outside of sharepoint (but can be deployed on the same server with sharepoint).
Is there any way i can programmatically pass the credentials (another domain account instead of 'NT AUTHORITY\NETWORK SERVICE' by default) to sharepoint when invoking OpenWeb method? I believe if i'm able to do that then i can walkaround the security issue above.
When you create your own custom virtual folder and set it inside the IIS, it's highly possible that the user account who run the application pool of that particular IIS virtual directory is currently set to NT authority\Network Service.
You can check carefully, by looking closely of what is the actual application pool that run that particular IIS virtual directory.
From there, you can go to the "Application Pool" folder and right click, choose Properties. Select the "Identity" tab, and it will show you who is the user account that currently running the application pool.
Alternatively, you can refer to the SharePoint SDK, something similar to ExtractCrmAuthenticationToken in dynamics CRM to extract the Authentication Token ticket.
Or alternatively you can use Network Credential to embed your own custom user id and password.
Hope this helps,
hadi teo
I fully agree with Hadi, if this is something you want to just quickly test, for a proof of concept, you can change the credentials under what the Application pool runs, to a user that has permissions. Or you could use Identity Impersonate setting in your config file.
However resist the temptiation to do this in a production enviroment, use the proper authentication. It will come back, to bite you.
If you need to set this up for production, there is a couple of areas that you want to look at, duplicate SPN's, and deligation probably the most common areas that is not configured correctly. Your error however points to impersanation not happening.
Also make sure you are deploying the web service to its own web site that does not already run SharePoint. If you want the web service to run on the same web site as SharePoint read Creating a Custom Web Service.
You can check what application pool identity SharePoint is using by following the same instructions that Hadi writes, but for an app pool running SharePoint. Make sure to only change the application pool used by your web service and not SharePoint or else other permission errors could occur within SP. (There should be no reason but if you are interested in changing the app pool identity used by SharePoint follow these instructions.)
On solution would be to "impersonate" as the SharePoint System account using the following code:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
// also dispose SPSite
using (SPSite siteCollection = new SPSite("http://localhost:89"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
// ...
}
}
});