This project is read-only.

C# JSDL generator and HTTP handler

The C# JSDL generator generates a JSDL definition from a WCF service implementation. There is also a class which implements a HTTP handler, which automatically processes HTTP requests and call the corresponding web service operation.

Its best to check out the sample: Open the Visual Studio JSDL solution and check out JsdlSampleWeb (and JsdlSampleApp).

Sample project directory structure:
  • web.config
  • Services
    • DataService.svc
    • JsdlServicesHandler.cs

web.config:

	<system.webServer>
		<handlers>
			<add name="Jsdl" verb="*" path="JsdlServices" 
				type="JsdlSampleWeb.Services.JsdlServicesHandler" 
				resourceType="Unspecified" />
		</handlers>
	</system.webServer>

JsdlServicesHandler.cs:

namespace JsdlSampleWeb.Services
{
	public class JsdlServicesHandler : JsdlServicesHandlerBase
	{
		public override Type GetServiceType(string serviceName)
		{
			return Type.GetType("JsdlSampleWeb.Services." + serviceName);
		}
	}
}

DataService.svc:

namespace JsdlSampleWeb.Services
{
	[ServiceContract]
	public class DataService
	{
		[OperationContract]
		public decimal Sum(decimal a, decimal b)
		{
			return a + b; 
		}

		[OperationContract]
		public Person GetPerson(int id)
		{
			return new Person { FirstName = "Foo", LastName = "Bar" };
		}

		[OperationContract]
		public void UpdatePerson(Person person)
		{
			
		}
	}
}

Exceptions

It is possible to throw exceptions in the web service operation which are propagated to the client:

[OperationContract]
public Person GetCurrentPerson()
{
	...
	throw new JsdlException("my_code", "my message");
}

If using another exception type, the transmitted code is set to the exception type name and the message is read from the Message property of the exception.

Authentication

Implement the AuthenticatedJsdlServicesHandlerBase class:

public class JsdlServicesHandler : AuthenticatedJsdlServicesHandlerBase
{
	public const int BlockingTime = 60; 
	public const int MaxLoginTries = 5; 

	public override Type GetServiceType(string serviceName)
	{
		return Type.GetType("MyProject.Web.Services." + serviceName);
	}

	public override bool CheckUser(string username, string password)
	{
		username = username.ToLower();
		using (var ctx = new DomainEntities())
		{
			var person = ctx.Persons.
				SingleOrDefault(p => p.Username == username && 
					p.IsActive);
			if (person != null)
			{
				if (person.LastLoginTry.HasValue && 
					(DateTime.Now - person.LastLoginTry.Value) > 
						TimeSpan.FromMinutes(BlockingTime))
				{
					person.LoginTries = 0;
					person.LastLoginTry = null; 
				}

				if (person.LoginTries >= MaxLoginTries)
					throw new SecurityException("account_blocked");

				if (!person.CheckPassword(password))
				{
					person.LoginTries++;
					person.LastLoginTry = DateTime.Now;
					ctx.SaveChanges();
					return false;
				}
				return true; 
			}
		}
		return false;
	}

	public override bool IsUserInRole(string username, string role)
	{
		return true; // TODO your implementation
	}
}

Sample implementation which blocks the user after five login tries for one hour...

Important: This authentication mechanism currently uses HTTP basic authentication which transmits the username and password in plain text! Use this method only over encrypted connections (namely SSL), otherwise it is very insecure.

Client side basic authentication

To authenticate the service client class with a username and password using basic authentication simply create a partial class for the service client class as shown in the following listing (for a sample DataService web service): (This technique is needed for the authentication mechanism used by the HTTP handler described above)

public partial class DataServiceClient
{
	public static DataServiceClient Create()
	{
		var svc = new DataServiceClient(AppSettings.ServerBaseUrl);
		svc.Username = AppSettings.Username;
		svc.Password = AppSettings.Password;
		return svc; 
	}

	public static DataServiceClient CreateAnonymous()
	{
		return new DataServiceClient(AppSettings.ServerBaseUrl);
	}

	public string Username { get; set; }
	public string Password { get; set; }

	partial void InitializeRequest(HttpGetRequest request)
	{
		if (!string.IsNullOrEmpty(Username))
			request.SetBasicAuthenticationHeader(Username, Password);
	}
}

... and create new service client instances with the static methods Create and CreateAnonymous.

IIS server configuration

Exceptions are not transmitted correctly

Problem: The exception code is always "error_*" and the message is empty and not your values specified in the exception thrown in your web service operation.

You have to enable HTTP content pass-through on your server for HTTP status errors. By default this is enabled, but it is possible that your web space provider overrode this configuration. Simply add the following httpErrors tag to your Web.config:

<system.webServer>
	<httpErrors existingResponse="PassThrough" />
</system.webServer>


Last edited Jul 31, 2013 at 3:19 PM by rsuter, version 12

Comments

No comments yet.