ict.ken.be

Delivering solid user friendly software solutions since the dawn of time.

Building a SPA with html5, web api, knockout and jquery - Notes 

Categories: CSS Javascript Notes WebApi

Building single page apps with html5, asp.net web api, knockout and jquery by JohnPapa.net and Pluralsight

 
Wonderful, just wonderful. This is one of the best and most complete tutorials I have ever seen. A must for every web developer. Non-developers should at least watch the application, to see how user friendly and responsive it is. The senior developer will get plenty of pointers to useful frameworks and libraries, while the beginner will get clear explanations and the why and how things are done. The included source code is very reusable and the only things that might need to be added to be able to use this in an enterprise environment are some secure token service and caching. (the use of breeze or upshot is recommended but not shown in the tutorial, probably because these frameworks are very new?) Enjoy. 
Why SPA?
  • As rich and responsive as a desktop app
  • Persisting important state on the client
  • Mostly loaded in the intitial page load
  • Progressively downloads features as required
  • 2-way data binding (MVVM)
  • Tombstoning / dropped connections 
1.CODE CAMPER
Technologies used:
  • CSS
  • LESS
  • Media Queries and responsive design
  • HTML5
  • Modernizr
  • HTML5 Boilerplate
  • AMD - Async Module Definition
  • Revealing Module Pattern
  • Prototypes
  • MVVM
  • jQuery - DOM/AJAX (+jquery.mockjson +jquery.activity)
  • knockout.js - Data Binding/MVVM (+Commanding +ChangeTracking +Templating +Validation)
  • amplify.js - Data Push/Pull, Client Storage, Messaging
  • Sammy.js - Nav/History
  • require.js - Dependency Resolution
  • underscore.js - Javascript Helpers
  • moment.js - Date parsing and formatting
  • toastr.js - UI Alerts
  • qunit - Testing
  • POCO Models
  • JSON and AJAX
  • Web API
  • Unit of Work Pattern
  • Repository Pattern
  • Single Responsibility Principle (SRP)
  • Entity Framework (EF Code First)
  • SQL Database
  • NuGet
  • JSON.NET
  • Ninject IoC
  • Web Optimization
2. TOOLS
  • JsFiddle.com / JsBin.com
  • Sublime / Notepad++
  • Mindscape - less in visual studio
  • Resharper
  • Responsinator.com - check in different sizes
  • ElectricPlum.com - iphone emulator
  • http://jpapa.me/responsivedesignbookmarklet
  • Web Standards Visual Studio Plugin
  • Web Essentials Visual Studio Plugin
3.DATA LAYER (EntityFramework 5.0)
SessionBrief (uses IDs)
Session: SessionBrief (gets full properties)
[Key] (uses Id as default)
DbSet<T>
EntityTypeConfiguration<T>
HasRequired( ).WithMany( ).HasForeignKey( ) 
override OnModelCreating : modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
CodeCamperDbContext : DbContext
{
   public CodeCamperDbContext() : base(nameOrConnectionString: "CodeCamper") { }
   public DbSet<Session> Sessions { get; set; }
}
Database.SetInitializer( );
CreateDatabaseIfNotExists<T>
DropCreateDatabaseIfModelChanges<T>
HasKey
EnityTypeConfiguration
HasRequired (s => s.Speaker).WithMany(p => p.SpeakerSessions).HasForeignKey(s => s.SpeakerId);
SRP - Single Responsibility Principle
public interface IRepository<T> where T : class
{
   IQueryable<T> GetAll();
   T GetById(int id);
   void Add(T entity);
   void Update(T entity);
   void Delete(T entity);
   void Delete(int id);
}
public class EFRepository<T> : IRepository<T> where T : class
{
   protected DbContext DbContext { get; set; }
   protected DbSet<T> DbSet { get; set; }
   public virtual T GetById(int id)
   {
      return DbSet.Find(id);
   }
  ...
}
Unit of Work Pattern decouples webapi controllers from the repositories and dbcontext
4. JSON with WebApi
Ninject IoC Container
kernel = new StandardKernel();
Kernel.Bind<ICodeCamperUow>().To<CodeCamperUow>();
..
config.DependencyResolver = new NinjectDependencyResolver(kernel);
 
[ActionName("rooms")]
GetRooms()
[ActionName("timeslots")]
GetTimeslots()
 
get throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
put return new HttpResponseMessage(HttpStatusCode.NoContent);
post 
response = request.createresponse(HttpStatusCode.Created);
response.Headers.Location = new Uri(Url.Link(RouteConfig.ControllerAndId, new { id = ...
testing : endpoints respond with success? return expected data? crud works?
config.Formatters.Remove(config.Formatters.XmlFormatter);
 
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
 
[Required(ErrorMessage = "Title is required")]
config.Filters.Add(new ValidationActionFilter());
//Puts the model errors in a JObject and returns them with
content.Response.CreateResponse<JObject>(HttpStatusCode.BadRequest, errors);
5. SPA Frame
 
Fallback for jQuery if CDN fails
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="script/lib/jquery-1.7.2.min.js"><\/script>')</script>
 
To activate bundles, the compilation debug has to be false.
bundles.UseCdn = true 
Modernizr goes separate since it loads first
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include("~/Scripts/lib/modernizr-{version}.js"));
@Scripts.Render("~/bundles/modernizr") 
bundles.Add(new ScriptBundle("~/bundles/jsmocks").IncludeDirectory("~/Scripts/app/", "*.js", searchSubdirectories:false);
 
bundles.Add(new StyleBundle("~/Content/css").Include(...);
bundles.Add(new StyleBundle("~/Content/less", new LessTransform(), new CssMinify()).Include("~/Content/styles.less"));
@Styles.Render("~/Content/css","~/Content/less")
6. Javascript Structure
  • Who is responsible for what?
  • Global scope pollution
  • High code re-use
  • Separation of concerns
  • Revealing Module Pattern
  • Bootstrapper.js will prime the data and setup the presentation
 
jQuery's deferreds, promises and $.when() at http://api.jquery.com/jquery.when/
 
ModularDemo to show js module dependencies: http://requirejs.org
define('alerter', ['dataservice'], function (dataservice) { ... });
requirejs.config({ baseUrl:'scripts', paths: { 'jquery' : 'jquery-1.7.2' } });
7. MVVM & KNOCKOUT
<input class="filter-box" type="text" 
data-bind="value: sessionFilter.searchText, 
valueUpdate: 'afterkeydown', 
escape: clearFilter"
placeholder="filter by model and brand"/>
 
_.reduce(timeslots, function (memo, slot) {
...
day = moment(slotStart).format('ddd MM DD');
...
}
 
configureExternalTemplates = function () {
infuser.defaults.templatePrefix = "_";
infuser.defaults.templateSuffix = ".tmpl.html";
infuser.defaults.templateUrl = "/Tmpl";
}
 
ko.bindingHandlers.escape example
 
self.searchText = ko.observable().extend({ throttle: config.throttle });
 
WebEssentials Plugin
ko.bindingHandlers example for favorites icon
 
sessions = ko.observableArray().trackReevaluations()
 
Debugging Tip
<div style="border: white dotted thin; clear: both">
<h4>Sessions JSON</h4>
<pre data-bind="text:ko.utils.debugInfo(speaker())"></pre>
</div>
ko.utils.debugInfo = function (items) {
return ko.computed(function() {
return ko.toJSON(items, null, 2); // JSON.stringify(ko.toJS(timeslots), null, 2);
}
}
8. Data Services on the Client
  • DataContext, DataService and Model Mappers
  • SRP - Single Responsibility Principle
  • datacontext.sessions.getData({data: sessions});
 
It's faster to create the raw array and then stick it into an ko.observableArray, so you get only one event notification !
 
  • $.ajax() vs $.getJSON()
  • http://amplifyjs.com : Wraps Ajax calls, Always Async, Mock Data, Caching
var init = function () {
amplify.request.define('session-briefs', 'ajax', {
url: 'api/sessionbriefs',
dataType: 'json',
type: 'GET'
//cache: true
//cache: 60000 // 1 minute
//cache: 'persist'
});
}
dataservice.session.js vs mock.dataservice.session.js
$.mockJSON.data.TITLE = [
'Pick and choose from these lines',
'Bla bla bla',
  'more bla bla' 
];
 
var data = $.mockJSON.generateFrom Template({
 'sessions|100-120' : [{
'id|+1': 1,
title: '@TITLE',
code: '@LOREM', //just one word
'speakerId|1-50':1,
description: '@LOREM_IPSUM' //just some text  
  }]
});
 
//some hardcoded to do some testing against
data.sessions[0].id = 1;
data.sessions[0].title = 'Single Page Apps';
 
if (_useMocks) {
dataserviceInit = mock.dataserviceInit;
}
dataserviceInit();
 
config.useMocks(true); //can be used in your tests
Role of the Data Context
  • Caches client data (Stores data in memory)
  • Facade for Data Services (Simplified API)
  • HandlesDataRequestLogic (Cached then return else get and cache) 
datacontext.sessions.getData(dataOptions(false))
sessions = new EntitySet(dataservice.session.getSessionBriefs, modelmapper.session, model.session.Nullo); 
EnititySet has mapDtoToContext, add, removeById, getLocalById, getAllLocal, getData, updateData
 
Prototypes in Javascript
 
Upshot.js 
  • Uses DataController that inherits from ApiController
  • Can get meta data from your WCF services
  • From the asp.net team
var dataSource = upshot.RemoteDataSource({
    providerParameters: { 
        url: constants.serviceUrl, 
        operationName: "GetProducts" 
    },
    provider: constants.provider,
    entityType: "Product:#Sample.Models"
}).refresh();
Breezejs.com
manager = new entityModel.EntityManager('api/todos');
var query = new entityModel.EntityQuery()
.from("Todos")
.orderBy("CreatedAt");
query = query.where("IsArchived", op.Equals, false);
  • Uses a Metadata action method on your controller (contextProvider.Metadata());
9. Navigation between views
sammy.get("#/sessions/:id', function (context) {
vm.callback(context.params);
presenter.transitionTo( $(viewName), path );
}
  •  jQuery event delegation to the container (performance)
  • bindEventToList(config.viewIds.sessions, sessionBriefSelector, callback, eventName)
  • resharper ctrl-shift-f7 and ctrl-alt-down will find the selected word in file.
$(rootSelector).on(eName, selector, function() {
var session = ko.dataFor(this);
callback(session);
return false;
});
Adding page transitions
  • css3-transitions: http://caniuse.com or http://realworldvalidator.com
  • jQuery transitions if you want to go cross-browser
  • presenter.transitionTo();
 
Browser history
<button class="button-back" data-bind="command: goBackCmd"></button>
 
goBackCmd = ko.asyncCommand({
execute: function (complete) { router.navigateBack(); complete() },
canExecute: function (isExecuting) { return !isExecuting && !isDirty(); }
});
ko.bindingHandlers.command = { ... };
 
Cancel & Recall using amplifyjs
  • module amplify.store for client storage on old and new browsers
  • messenger module amplify.publish and amplify.subscribe
 
If changes and user clicks back button, we use toastr to display a warning message (http://nuget.org/packages/toastr).
sammy.before(/*/, function () { ... response = routeMediator.canLeave(); ... }
 
store.save(config.stateKeys.lastView, context.path);
This module wraps amplify, so it can easily be replaced with localstorage on HTML5
expires: config.storeExpirationMs in milliseconds
 
Alternatives for sammy.js
10. Saving, Change Tracking and Validation
model.person.js
self.dirtyFlag = new ko.DirtyFlag([
self.firstName,
self.lastName,
self.email,
]);
isDirty = ko.computed(function() {
return canEdit() ? speaker().dirtyFlag().isDirty() : false;
});
 
Asynchronous Commands 
(eg. disable save button once it is clicked and put status indicator)
saveCmd = ko.asyncCommand({
execute: function(complete) {
$.when(datacontext.person.updateData(speaker()))
.always(complete);
},
canExecute: function(isExecuting){
return !isExecuting && isDirty() && isValid();
}
});
<button data-bind="command: saveCmd, activity: saveCmd.isExecuting'>Save</button>
Explicit vs Implicit Save?
saveFavorite = function (selectedSession) {
if (isBusy) { return; }
isBusy = true;
var cudMethod = selectedSession.isFavorite()
? datacontext.attendance.deleteData
: datacontect.attendance.addData;
cudMethod(selectedSession,
{
success: function() { isBusy = false; },
error: function() { isBusy = false; }
}
);
}
 
Use valueHasMutated() to tell and observable its value has updated.
 
Validate Data?
 
  • Using Knockout.Validation plugin.
self.firstName = ko.observable().extend({ required: true });
self.email = ko.observable().extend({ email: true });
self.blog = ko.observable().extend({
pattern: {
message: 'Not a valid url',
params: /[@]([A-Za-z0-9_]{1,15})/i
}
});
 
validationErrors = ko.validation.group(speaker());
isValid = ko.computed(function() {
return canEdit() ? validationErrors().length == 0 : true;
});
canLeave = function() {
return canEdit() ? !isDirty() && isValid() : true;
}
11. Mobility and Responsive Design with CSS and Less
  • Balsamiq to do mocks of gui
  • Web Essentials in Extensions and Updates of Visual Studio 2012
  • http://lesscss.org
  • Less: Variables, Mixins, Nested Rules, Functions and Operations
  • Media Queries
  • Testing for multiple devices: http://jpapa.me/electricplum 
@media only screen and (max-width: 1000px)
@media only screen and (max-width: 900px)
@media only screen and (max-width: 800px)
@media only screen and (max-width: 480px)
@media only screen and (max-width: 320px)
@media only screen and (max-width: 240px)
 
@media only screen and (max-height: 768px) {
.view-list { height: 330px; }
}

 

MVC4 Fundamentals and the WebApi - Notes 

Categories: Notes WebApi
MVC4 Fundamentals and the WebApi
Scott Allen

I only looked at this module of the course. John Papa referred to it in his single page application course. Not much new except if you didn't know about curl and handlebars.

  • System.Web.Http
  • System.Web.Http.WebHost
  • GET, POST, PUT, DELETE by default
  • [AcceptVerbs] for others

Scott preferes using a plural name. Eg. VideosController

cUrl
  • curl http://somewebsite/api/videos
  • curl http://somewebsite/api/videos -X GET
  • curl http://somewebsite/api/videos -X DELETE

-> File not found exception, then IIS needs to be configured

-> IISExpress applicationhost file add on ExtentionlessUrl the verbs

  • curl http://somewebsite/api/videos -X GET -v
  • curl http://somewebsite/api/videos -X GET -H "Accept: application/json"
  • curl http://somewebsite/api/videos -X POST -d "value=scott"
  • curl http://somewebsite/api/videos -X POST -d "=scott"
Content negotiation
  • Same resource can have multiple representations.
  • Accept: image/png
  • Accept: Text/html 
Action Parameters
  • Primitive types assumed to not be in the message body, unless public void Post([FromBody]string value) {}
  • Complex types assumed in the message body
  • Only a  single model allowed from the message body
EF NuGet 
  • enable-migrations
  • Seed(... AddOrUpdate( ) )
  • update-database
  • db.Configuration.ProxyCreationEnabled = false;
Showing Data in view
  • Update-Package jQuery
  • Update-Package HandleBars
  • <script> type attribute is no longer needed in Html5

var videoApiUrl = '@Url.RouteUrl("DefaultApi", new { httproute="" , ... '

//global error handler

$(document).ajaxError(function (event, xhr) {

alert(xhr.status + ":" + xhr.statusText);

}

<script id="template" type="text/html>

{{#each video}}

{{Title}}

{{/each}}

</script>

Handlebars.compile($("#videoTable").html());

public Video GetVideo(int id)

{

   var video =  db.Find(id); 

   if (video==null)

   {

     throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));

   }

   return video;

}

public HttpResponseMessage PutVideo(int id, Video video)

{

   db.Entry(video).State = EntityState.Modified;

.. HttpStatus NotFound OK BadRequest

}

On post set 201 Created and add response.Headers.Location

On Delete check for DbUpdateConcurrencyException

install-package Microsoft.AspNet.WebApi.Client

var client = new HttpClient();

client.DefaultRequestHeaders.Accept.Add(

   new MediaTypeWithQualityHeaderValue("application/xml"));

var result = client.GetAsync(new Uri(...))

 

TCP/IP Networking for Developers - Notes 

Categories: Network Notes
TCP/IP Networking for Developers
Steve Evans - http://sevans.info
This was by far the worst Pluralsight tutorial I have ever seen, I am not an expert in networking and had hoped to learn a thing or two. Unfortunately, the course was not only very unstructured but also not very enlighting. Not even for a computer noob.
 
  • ipconfig
  • ipconfig /all | more
  • ipconfig /displaydns

  • c:\windows\system32\drivers\etc\hosts
  • You can put multiple hostnames on one line
  • 127.0.0.1 ken.be patrycja.pl test.be

  • No DHCP then 169.254.x.x

 

  • Change your ip logging to ipV6
 
Name resolution
  • nslookup ken.be
  • nslookup + enter, server 8.8.4.4
 
  • a-record transforms hostname into ip
  • set type=NS (nameserver)
  • set type=MX (mailexchange)
  • set type=CN (canonicalname or alias)
  • set type=AAAA (quad a) returns ipv6
  • wildcards records
 
A router connects different subnets
  • tracert
  • pathping
 

subnets

  • 255.255.255.node
  • 255.255.node
  • 255.255.255.240
 

routes

  • 0.0.0.0 means any ip address (netmask 0.0.0.0 to gateway)
  • 127.anything is always the localhost (netmask 255.0.0.0)
  • gateway.0 all that are on-link
  • 255.255.255.255 broadcast that doesn't cross router
  • route print
 
NAT (Network Address Translation)
  • Private Network Ranges
  • 10.0.0.0/255.0.0.0
  • 172.16.0.0/255.240.0.0
  • 192.168.0.0/255.255.0.0
 
Port Connectivity
  • TCP (Transmision Control Protocol) - request missing
  • UDP (User Datagram Protocol) - no check if received - no session
 
  • telnet ken.be 80
  • 400 Bad Request
  • port 1433 standard mssql
  • port 25 smtp
 
  • nmap -v servername (zenmap is win gui)
  • netstat -ano
 
Windows firewall
  • Log dropped packets
 
ICMPv4 protocol used by ping
Network Capture
Wireshark : right-click and follow tcp stream
Fiddler
 

 

Introduction to the ASP.Net WebApi - Notes 

Categories: Notes WebApi

Introduction to the ASP.NET WebApi
Jon Flanders

  • Service layer for HTML5, mobile applications, ...
  • REST : Representational State Transfer (Roy Fielding)
  • Full rest -> Level4 service
RouteTable.Routes.MapHttpRoute(
"InstructorRoute",
"{controller}/{id},
new { id = RouteParameter.Optional }
);
 
ApiController
[Authorize(users="jon")]
public string Get() //returns all
{
  return this.Request.GetUserPrinciple().Identity.Name;
}
public HttpResponseMessage<Instructor> Get(int id)
{
   var instructor = (from ...)
   if (null == instructor)
   {
      //return 404
      var notfoundMessage = new HttpRequestMessage<Instructor>(HttpStatusCode.NotFound);
      return notfoundMessage;       
   }
   var newMessage = new HttpRequestMessage<Instructor>(instructor);
   return newMessage;
}
public HttpResponseMessage<Instructor> Post(Instructor instructor)
   //create and insert and return 201 
   var newMessage = new HttpRequestMessage<Instructor>(instructor, HttpStatusCode.created);
   newMessage.Headers.Location = 
      new Uri(Request.RequestUri, "/instructors/" + instructor.ID.ToString());
}
public Instructor Put(Instructor instructor)
{ update }
public Instructor Delete(int id)
{ remove }
System.Net.Http
System.Net.Http.Formatting
System.Web.Http
System.Web.Http.Common
System.Web.Http.WebHost
System.Web.Http.SelfHost
System.Web.Http.Data
System.Web.Http.Data.EntityFramework
System.Web.Http.Data.Helpers 
  • ModelBinder for Body, header, querystring
  • MediaTypeFormatters
  • Validation via attributes
  • HttpRequestMessage and HttpResponseMessage
  • Content negotiation (xml or json)
  • Customize file extension or querystring
  • Fidler : Post with Content-Type:application/json
  • http://odata.org with IQueryable
  • HttpConfiguration class
  • GlobalConfiguration.Configuration
  • DependencyResolver 
Uniform interface of REST
  • Start with URI, add well-known HTTP verbs
  • Get: cacheble, safe, always same effect (idempotent)
  • Post: create new resource, unsafe, non-cached 
  • Put: update, idempotent
  • Delete: remove, idempotent
  • You can specify multiple verbs to one method
ApiController: IHttpController, IDisposable
{
   Configuration, ControllerContext, ModelState, Request, Url, Task<HttpResponseMessage>, ExecuteAsync, Initialize
}
 
HttpRequestMessage:IDisposable
{
  Content, Headers, Method, Properties, RequestUri, Version, Displose
}
 
HttpResponseMessage:IDisposable
{
  Content, Headers, IsSuccessStatusCode, ReasonPhrase, RequestMesage, StatusCode, Version, Dispose, EnsureSuccessStatusCode
}
$.ajax({
url: '/api/foo/id',
success: function (data) {},
statusCode: { 
404: function() {} 
}
}); 
HttpClient
  • HttpWebRequest, WebClient
var c = new HttpClient();
c.GetAsync(uri).ContinueWith((t) =>
{
  HttpResponseMessage response = null;
  response = t.Result;
  response.EnsureSuccessStatusCode();
  response.Content.ReadAsAsync<JsonObject>().ContinueWith(readTask) =>
{
  ProcessJson(readTask.Result);
}
});
  • http://search.twitter.com/search.json?q=fun
  • PostAsync, PutAsync
  • ObjectContent<T> for Json or XML
  • StreamContent for raw data
  • MultipartContent and MultiPartFormContent
  • ByteArrayContent, FormUrlEncodedContent, StringContent
  • UploadVideoClient (HttpClient)
  • Dealing with headers
  • Dealing with cookies 
Per-request headers
var pcontent = new HttpRequestMessage<Person>().CreateContent<Person>(new Person());
pcontent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
pcontent.Headers.Add("Accept","application/xml");
  • public class HttpClientHandler : HttpMessageHandler
  • Derive from DelegatingHandler
  • The HttpClient does not send a UserAgent by default
  • client.DefaultRequestHeaders.Add("User-Agent","Jon's Cool Client");
  • Token-based authorization can be accomplished by a custom HttpClientHandler
Self-Hosting Example
  • Create a HttpConfiguration (eg. HttpSelfHostConfiguration)
  • Create your HttpServer (eg. HttpSelfHostServer)
  • Start listening: HttpServer.OpenAsync();
  • When using the first contructor overload, you end up with HttpControllerDispatcher as your HttpMessageHandler. It allows you to use the same ASP.NET routing and same convention or configuration settings as ApiController in ASP.Net Hosting.
  • config.Routes.MapHttpRoute("DefaultRoute", "{controller}/{id}", new { id=Parameter.Optional });
  • You can pass HttpServer to HttpClient, useful for self-hosting testing (all in memory)
var client = new HttpClient(server);
client.GetAsync(...).ContinueWith(
(t)=>{ var result = t.Result; result.Content.ReadAsStringAsync().ContinueWith(
(tr) => { Console.Write(rt.Result);})
;});
Security
ASP.NET Authentication or WCF for self-hosting
 
AllowAnonymousAttribute : explicitly allow access to action of apicontroller
 
Forms authentication
  • /account/jsonlogin endpoint with MVC4 projects
  • Authentication_JSON_AppService.axd
  • App gets cookie
 
Basic authentication
 
  • HttpClient/WebHttpRequest/WebClient understand Windows authentication
 
You can add support for token authentication (eg OAuth) fairly easily
 
Authorization
  • AuthorizeAttribute with names or roles on individual actions (returns 401 if unauthorized)
Self-host
  • HttpMessageHandler
  • request.GetUserPrincipal().Identity.Name;
  • HttpSelfHostConfiguration
protected override BindingParameterCollection OnConfigureBinding(HttpBinding httpBinding)
{
   httpBinding.Security.Mode = HttpBindingSecurity.TransportCredentials;
   httpBinding.Security.Transport.CredentialType=HttpClientCredentialType.Windows;
   return base.OnConfigureBinding(httpBinding);
Asp.Net Hosting
  • Global.asax.cs
  • GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Extensibility
  • GlobalConfiguration.Configuration
  • HttpConfiguration
  • Filters, Formatters, MessageHandlers, Routes, ServiceResolver
  • IncludeErrorDetailPolicy.Always / Never / LocalOnly
 
Formatters will match incoming Content-Type with a MediaTypeFormatter (content negotiation and serialization)
  • JsonP
  • Inject code that isn't in same domain as controller
  • HttpResponseMessage<JsonPReturn> Get(int id, string callback)
  • response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/javascript");
<script>
function myCallback(obj) { document.getElementById('msg').innerText = obj.Data; }
</script>
<script src="http://server.com/api/controller/values/1?callback=myCallBack" />
Filters hook into the model binding and controller execution pipeline (model-related issues)
  • AuthorizeAttribute is an IFilter
  • MyFilter : IActionFilter
  • actionContext.ActionArguments 
 
Message Handlers are the http processing pipeline base class (http-issues)
  • HttpServer, HttpClientHandler, HttpControllerDispatcher
public class MyMethodOverrideHandler : DelegatingHandler
{
   const string header = "X-HTTP-Method-Override";
   //override SendAsync 
   //and let clients put the real verb in this header
}
ServiceResolver (internal implementation detail extensibility)
  • Implement IDependencyResolver or Call SetResolver and pass in two delegates
Action Filter vs. Mesage Handler
  • MessageHandler - before model binding - HTTP message level - eg. run an xml Schema validation
  • ActionFilter - after model binding - Extending your controller - eg. run an object validation

 

Install TortoiseHG - Mercurial on Windows IIS 

Categories: Mercurial

Well, I can tell you... this is a painful adventure and for sure not worth saving 7$ a month. But sometimes you have to do what you have to do... Good luck.

Install Python
Install Python Windows Extensions (PYWIN32)
Intall and compile Mercurial source
  • http://mercurial.selenic.com/release/mercurial-1.8.4.tar.gz
  • c:\mercurial-source-1.8.4\
  • python.exe setup.py --pure build_py -c -d . build_ext -i build_mo --force
  • python.exe setup.py --pure install --force
  • This will build and install Mercurial into the Python install folder eg. C:\Python27\Lib\site-packages\mercurial
  • Add ;C:\Python27\scripts\ to the environment path
  • Test by typing hg in command window
Setup Mercurial HgWeb Host on IIS 7
  • http://code.google.com/p/isapi-wsgi/downloads/detail?name=isapi_wsgi-0.4.2.zip&can=2&q=
  • python setup.py install
  • http://selenic.com/repo/hg-stable/raw-file/1e6661e09818/contrib/win32/hgwebdir_wsgi.py
  • Create the file folder that will be hosting your Mercurial IIS7 repository website. Eg. C:\Websites\hg\
  • Copy hgwebdir_wsgi.py file to the website folder.
  • Open hgwebdir_wsgi.py for editing by right clicking it and using the Python editor
  • Set the hgweb config value to the website folder. hgweb_config = r'c:\Websites\hg\hgweb.config'
  • Unless you are using a virtual application/directory set the path prefix to zero then save/close the file.path_prefix = 0
  • Create a new hgweb.config file in the website folder with the following content: 

    [paths]
    / = c:\Repository\*
     
  • Open a DOS prompt and set the directory to your website folder. Eg. c:\Websites\hg\
  • Execute the following to compile a DLL called _hgwebdir_wsgi.dll: python hgwebdir_wsgi.py
  • Create a new application pool in IIS Manager called “Mercurial-Repo”
  • Set the .net framework version to “No Managed Code”
  • Create a new website in IIS Manager pointing it to the website folder. Be sure to select the Mercurial-Repo application pool.
  • Open up the Handler Mappings for your new web site.
  • Add a new “Wildcard Script Map” with the Executable location pointing to the _hgwebdir_wsgi.dll in the website folder. Name it Mercurial-ISAPI
  • Click the OK button, and click YES to allow the ISAPI extension.
  • Assuming you have followed all directions carefully browsing to the newly created website should serve the empty repositories listing!
Using HG examples
  • hg init c:\Repository\test
  • hg clone http://sourcecontrol.yourserver.com/test
Pushing Changes
    • By default, all repositores served via hgwebdir are read only – you cannot push changes to them. To change this, we can specify the users that should be able to push to the repositores by adding an "allow_push" section to our hgweb.config:
      [web]
      allow_push = Ken, Patrycja
      push_ssl = false
SSL Certificate and Self-Signing With TortoiseHG and IIS
    • Install SSL certificate and remove push_ssl = false
    • hg push --insecure https://self-signed-host/repo
    • C:\Program Files\TortoiseHg\hgrc.d
      [web]
      cacerts=
IIS Config for File-size, Time-Outs and blocked extensions
    • In IIS Manager, click on the web site node, and click the Limits… link. Put time-out higher.
   <?xml version="1.0" encoding="UTF-8"?>
     <configuration>
       <system.webServer>[…]
       <security>
         <requestFiltering>
           <requestLimits maxAllowedContentLength ="2147482624" />
           <fileExtensions>
             <remove fileExtension=".cs" />
           </fileExtensions>
         </requestFiltering>
       </security>
     </system.webServer>
     <system.web>
       <httpRuntime executionTimeout="540000" maxRequestLength="2097151"/>
     </system.web>
   </configuration>
Error: [Errno 10054] An existing connection was forcibly closed by the remote host

This is a problem with the SSL module in Python 2.7.3/4 - it doesn't like IIS's SSL implementation. If you disable SSL v2 on your iis it will be solved. (IISCrypto)

Other ways of setting up Mercurial on Windows IIS 
About Mercurial - TortoiseHG
Skip all this and get it from the cloud
Page 30 of 43 << < 20 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 40 > >>