Tilbake til hovedsiden

Seo analyse – Bedrifts Webdesign – IT support – Optimalisering

Finn webside relaterte utrykk og ord

Defining Routes using Regular Expressions in ASP.NET MVC

Author: Fredrik Kalseth

Having played about with the recently released beta 2 of the ASP.NET MVC framework, I was kind of disappointed by the still fairly restrictive routing abilities of the framework. Currently, setting up a route for a request is done using a tokenized string, allowing each token to map to a controller, action and parameters on the action. This works fine in most situations, but can be frustratingly restrictive in others.

In a post detailing the routing system of the framework, Scott Guthrie also answered a comment which asked why they weren’t using regular expressions for defining routes by claiming that only a subset of people really know how to use them. He did however allow for the possibility that it might be added as an option later – but why wait when we can implement it ourselves? Here’s my quick and dirty RegexRoute class:

public class RegexRoute : Route
{
    private readonly Regex _urlRegex;

    public RegexRoute(string urlPattern, IRouteHandler routeHandler)
        : this(urlPattern, null, routeHandler)
    {}

    public RegexRoute(string urlPattern, RouteValueDictionary defaults, IRouteHandler routeHandler)
        : base(null, defaults, routeHandler)
    {
        _urlRegex = new Regex(urlPattern, RegexOptions.Compiled);
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        string requestUrl = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;

        Match match = _urlRegex.Match(requestUrl);

        RouteData data = null;

        if(match.Success)
        {
            data = new RouteData(this, this.RouteHandler);

            // add defaults first
            if (null != this.Defaults)
            {
                foreach (KeyValuePair<string, object> def in this.Defaults)
                {
                    data.Values[def.Key] = def.Value;
                }
            }

            // iterate matching groups
            for (int i = 1; i < match.Groups.Count; i++)
            {
                Group group = match.Groups[i];

                if (group.Success)
                {
                    string key = _urlRegex.GroupNameFromNumber(i);

                    if (!String.IsNullOrEmpty(key) && !Char.IsNumber(key, 0)) // only consider named groups
                    {
                        data.Values[key] = group.Value;
                    }
                }
            }
        }

        return data;
    }
}

Incidentally, a great “side effect” of having the route defined as a regular expression is that it allows us to perform validation in-place, which kind of renders the concept of Constraints unnecessary.

Here’s a quick example route defined as a regular expression:

routes.Add(new RegexRoute(@"^Books/((?<ssn>[\d]{3}(-?)[\d]{2}\1[\d]{4})|(?<query>.+)?)$", new MvcRouteHandler())
   {
       Defaults = new RouteValueDictionary(new { controller = "Book", action = "Find" })
   });

Now the route above could, and possibly should, have been split into two different routes; one for finding books by SSN and a different one for searching book titles, but I wanted to demonstrate the flexibility gained by using regular expressions. With the above route, urls like “mysite.com/Books/Last+Argument+Of+Kings” and “mysite.com/Books/0575077905” both map to the Find action on the BookController class, with the appropriate parameter initialized:

public class BookController : Controller
{
    public void Find(string query, int? ssn)
    {
        // ... gets the book by ssn if present, otherwise searches using the query
    }
}

Even with regular expression support, I’m still not 100% happy with the routing; it won’t allow me to overload controller actions. In the above example, for instance, I would much rather have two separate Find(string query) and Find(int ssn) methods, and then have the correct one invoked depending on whether the query or SSN parameter was present in the route – but the MvcRouteHandler doesn’t know how to resolve that and just throws an exception. Look out for another post investigating the implementation of a custom route handler for working around that, soon :)

 

Smarter Asynchronous Data Loading

Author: Fredrik Kalseth

An entry about windows forms Publication date 23. September 2007 13:23

Imagine you have a Windows Forms application that has a dialog for sending email messages. The ‘Send Message’ dialog has a button that opens a ‘Find Recipient’ dialog, which lets the user select a recipient from a rather large address book. It takes the data access layer several seconds to come up with the data for this dialog. Hopefully, warning lamps should go off in your head at seeing the following code:

private void FindRecipient_Load(object sender, EventArgs e)
{
_recipientsList.DataSource = AddressBook.GetRecipients();
}

Sure, it works – but since we are loading the data in the same thread as the UI, we’re effectively blocking the message pump which makes the application seemingly hang for the time it takes GetRecipients() to return the data. If this method takes 5 seconds, 10 seconds or even a few minutes to return, the user would be forgiven for thinking your application has crashed.

We can surely do better. Lets move the loading of data into a separate thread, and let the user know that it may take a while:

private void FindRecipient_Load(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
PleaseWait pleaseWait = new PleaseWait();
worker.DoWork += new DoWorkEventHandler(
delegate(object s, DoWorkEventArgs args)
{
args.Result = AddressBook.GetRecipients();
});
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate(object s, RunWorkerCompletedEventArgs args)
{
_recipientsList.DataSource = args.Result;
pleaseWait.Dispose();
});
worker.RunWorkerAsync();
pleaseWait.ShowDialog();
}

Here, we’re using the BackgroundWorker class to load the data asynchronously. That’s a huge improvement, and in many scenarios it will be good enough. But lets take it one step further…

What if we could deduce that the user is likely to soon open the dialog? Then we could start loading the data even before the user requests it, having it ready in time for when it is needed. In this example, we can confidently assume that when the user opens the ‘New Message’ dialog, he will shortly after want to select a recipient. Why not start loading the address book data right away?

private void NewMessage_Load(object sender, EventArgs e)
{
// here, we start populating the cache asynchronously because we know the
    // user is likely to click the 'Find' button soon. 
    AddressBook.PopulateCacheAsync(); 
}

Now, as soon as the parent dialog opens we start loading the data asynchronously. If the user now spends a few seconds typing in a title for his message, and then clicks the ‘Find’ button to find a recipient, the data will already have had time to load the data, and the FindRecipient dialog shows up almost instantly.

However, to make this work properly we need to synchronise the PopulateCacheAsync and GetRecipients methods on AddessBook – because if the user clicks the ‘Find’ button before PopulateCacheAsync has finished populating the cache, then it and GetRecipients will likely get in a tangle. Luckilly, this is quite easy to sort out using the double-checked locking pattern:

public static class AddressBook
{
private static readonly int _noOfRecipients = 500;
static Action<int> _reportProgress = null;
private static IList<string> _cache = null;
private static object _cacheLock = new object();
private static IEnumerable<string> GetMockData()
{
for (int i = 0; i < _noOfRecipients; i++)
{
yield return "Recipient #" + i;
Thread.Sleep(10); // sleep for 10ms each loop to simulate a long-running operation
        }
}
public static void PopulateCacheAsync()
{
ThreadPool.QueueUserWorkItem(
delegate
            {
PopulateCacheCore();
});
}
private static void PopulateCacheCore()
{
if (null == _cache) // if the cache has not already been loaded, populate it
        {
lock (_cacheLock)
{
if (null == _cache) // the cache may have been popualted while we were waiting to get the lock, so check again
                {
_cache = new List<string>(GetMockData());
}
}
}
}
public static IList<string> GetRecipients()
{
PopulateCacheCore();
return _cache;
}
}

And thats it! We’ve radically improved the perceived performance of our application by being a bit clever about when we load our data. I’ve included a demo solution that includes the above three scenarios, so you can get a feel for how they perform. The downloadable code also goes even further and adds a progres bar to the ‘please wait’ dialog.

Constructor Injection for ASP.NET MVC Action Filters

Author: Fredrik Kalseth

An entry about asp.net mvc | inversion of control Publication date 9. April 2009 11:50

ASP.NET MVC’s action filters allow you to execute some code before/after an action in a declarative manner, manipulating what goes into or comes out of the action. In essence, they are what’s called aspects in Aspect Oriented Programming; a way of encapsulating crosscutting concerns into reusable components. For example, instead of this:

public ActionResult SomeActionWhichRequiresAuthentication()

{

if (!User.Identity.IsAuthenticated)

{

return RedirectToAction(“Login”, “Authentication”);

}

else

{

// …

}

}

You’ll do this:

[Authorize]

public ActionResult SomeActionWhichRequiresAuthentication()

{

// …

}

However, a problem with ASP.NET MVC’s action filters is that there’s no support for having an IOC container manage them, which means that if I need to do something more complicated that requires calling into a domain service/repository for example, I cannot get these injected easily. Jeremy Skinner has a solution which enables property injection, but what I really want is constructor injection.

Solution: The Proxy Pattern

What I came up with, was to build a filter proxy. Using it looks like this:

[Filter(typeof(AuthorizationFilter))]

public ActionResult SomeActionWhichRequiresAuthentication()

{

// …

}

Internally, the FilterAttribute resolves the actual filter using the IOC container and forwards all its calls to it:

public class FilterAttribute : ActionFilterAttribute

{

private readonly Type _actionFilterType;

private IActionFilter _action;

 

public FilterAttribute(Type actionFilterType)

{

_actionFilterType = actionFilterType;

}

 

public override void OnActionExecuting(ActionExecutingContext filterContext)

{

// resolve the action filter using the IOC container

var container = (ICanResolveDependencies) HttpContext.Current.ApplicationInstance;

_action = (IActionFilter)container.Resolve(_actionFilterType);

 

_action.OnActionExecuting(filterContext);

base.OnActionExecuting(filterContext);

}

 

public override void OnActionExecuted(ActionExecutedContext filterContext)

{

_action.OnActionExecuted(filterContext);

base.OnActionExecuted(filterContext);

}

}

This allows the IOC container to manage the instantiation and lifetime of the action filter. Note the assumption that the applications Application object (Global.asax) implements the ICanResolveDependencies interface, which essentially is an abstraction of the IOC container.

Currently rated 3.3 by 9 people

  • Currently 3.333333/5 Stars.

Comments

4/9/2009 12:23:21 PM

Andrew Davey

Nice solution. Thanks for sharing.

Andrew Davey United Kingdom

4/10/2009 12:44:39 PM

Simone

Nice solution… but I think that having the ActionInvoker inject dependencies is a better solution.
If you make a use of ASP.NET MVC extensibility points you end up keeping out of the way all your own infrastructural code.
An example of how this was done is the the NinjectActionInvoker ( codeclimber.net.nz/…/…es-into-asp.net-mvc.aspx ).

Simone Italy

4/10/2009 4:47:39 PM

Fredrik

@Simone The NinjectActionInvoker you refer to is only able to do property injection, not constructor injection. In my opinion, property injection should only ever be used for optional dependencies. I agree that my proxy solution is not optimal, though. The best would be if ASP.NET MVC had native IOC support for all its extensibility points (like FubuMVC).

Fredrik Norway

4/10/2009 5:13:29 PM

Kazi Manzur Rashid

There is another issue by wrapping it in another filter, it is not possible to pass the parameters values of the original filter, for ex how do you pass the roles if your Authorize filter was decorated with it?

Kazi Manzur Rashid United States

4/10/2009 5:17:46 PM

Jeremy Skinner

I actually moved away from using the property-injection approach that I blogged about to something very similar to this. Here’s an example:

sutekishop.googlecode.com/…/…UsingAttribute.cs

The only real difference is that it supports multiple filter types rather than just Action Filters.

Jeremy

Jeremy Skinner United Kingdom

4/10/2009 5:35:39 PM

trackback

Trackback from DotNetShoutout

Constructor Injection for ASP.NET MVC Action Filters – Fredrik Kalseth

DotNetShoutout

4/10/2009 8:39:26 PM

Fredrik

@Rashid Good question. The proxy could be refactored to take an anonymous object and use it to set properties on the filter. It wouldn’t be too pretty, though. Like I said, I really wish the MVC framework had a pluggable container at its core so we wouldn’t need to hack it like this Smile

Fredrik Norway

4/13/2009 2:59:20 AM

trackback

Trackback from 9eFish

Constructor Injection for ASP.NET MVC Action Filters

9eFish

4/18/2009 1:23:50 PM

trackback

Trackback from Faculty of The Mind

Constructor Injection for ASP.NET MVC Model Binders

Faculty of The Mind

4/27/2009 11:28:43 PM

Jones

Nice post…

Jones United States

5/12/2009 11:55:24 AM

squig

Thank you for saving my day Smile

squig Germany

6/19/2009 8:34:02 PM

javier

What about using “compile time weaving” techniques ?. I’ve registered the filter in the IoC container with its corresponding dependencies. The filter class has to be decorated with the [Configurable(“FilterId”)] attribute so the weaver (PostSharp) knows about. Whenever whoever instantiates the class the dependencies get wired.

Sharing Variables Between JavaScript and C#

Author: Fredrik Kalseth

An entry about asp.net | javascript Publication date 11. January 2008 17:53

You know, I think the most difficult part of this blog post was coming up with a descriptive title for it – it just needs more words to be explained. The story goes like this: While working on a JavaScript-heavy web application, I found myself wanting to share a set of variables between my server side C# code and the client-side JavaScript code. I wanted to be able to set a value in the JavaScript code running in the clients browser, and then later retrieve that same value on the server in the code behind of the page upon the next postback – and vice versa. It might sound a bit tricky (or maybe not?), but it proved to be quite simple – in fact, its perfect for some Friday afternoon blogging indulgence, before we get started on dinner :)

Aim First…

Whenever I decide to create a component, the first thing I do is think about how I want to use it. I find writing some mock code the perfect way to settle on an interface for the component, before even writing a line of code for it (TDD, anyone?). This way, I’m sure I’m actually making what I need, and not what I think I might need. So, let’s look at some code that mimics what we want to accomplish with our component.

What I’d like to do, is to be able to drop it onto a Web Form and access it both from my JavaScript and my code behind. So, something like this:

 

<idc:ClientDictionary 
    ID="_myDictionary"
    runat="server" />

 

Having declared that in my markup, I want to be able to do this from the client-side JavaScript:

$find('_myDictionary').setValue("key", "value");

 

And then, when the form is submitted, I want to be able to retrieve that value from the server-side C#:

string value = _myDictionary.Values[“key”];

 

…and Fire at Will

Okay, so hopefully you’ve got an idea of what I’m up to. Basically, what I want is a hidden input field – but one that can store more than one value. Instantly, I’m thinking Asp.Net Ajax – or more precisely, a Control Extender. I could write one that targets the HiddenField control, transporting my dictionary between the client and server by serializing it into the hidden input field. But while that would take care of the client-side part, I need to extend the HiddenField serverside aswell. To do that, I’ll write a new component that wraps the HiddenField and the said extender control into a composite control, ready to be dropped onto a form and just work.

Without any further ado, lets look at the source code I came up with for this control:

public class ClientDictionary : CompositeControl
{
    private Dictionary<string,string> _values;
 
    /// <summary>
    /// The name of the dictionary, used for accessing the dictionary on the client using $find([name]).
    /// </summary>
    public string Name
    {
        get
        {
            EnsureChildControls();
            return _hiddenFieldExtender.BehaviorID; // we map the name to the behavior ID of the hidden field extender 
        }
        set
        {
            EnsureChildControls();
            _hiddenFieldExtender.BehaviorID = value;
        }
    }
 
    public override bool EnableViewState
    {
        get
        {
            // this control does not need view state
            return false;
        }
        set
        {
            throw new InvalidOperationException();
        }
    }
 
    /// <summary>
    /// Gets or sets the values stored in the dictionary
    /// </summary>
    public Dictionary<string, string> Values
    {
        get { return _values; }
        protected set { _values = value; }
    }
 
    protected override void OnInit(EventArgs e)
    {
        EnsureChildControls();
 
        // the hidden field contains our javascript dictionary, which we'll want to deserialize into a C# dictionary
        string serializedDictionary = this.Page.Request.Form[_field.UniqueID];
 
        if (!String.IsNullOrEmpty(serializedDictionary))
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            this.Values = serializer.Deserialize<Dictionary<string, string>>(serializedDictionary);
        }
        else
        {
            // no client-side dictionary was present, so create a new blank one
            this.Values = new Dictionary<string, string>();
        }
 
        base.OnInit(e);
    }
 
    private HiddenField _field;
    private ClientDictionaryExtender _hiddenFieldExtender;
 
    protected override void CreateChildControls()
    {
        _field = new HiddenField(); // we use the hidden field to transport our data between the server and client parts
        _field.ID = "fl";
        this.Controls.Add(_field);
 
        // the extender makes sure a client-side behavior component representing the dictionary is available
        this._hiddenFieldExtender = new ClientDictionaryExtender();
        this._hiddenFieldExtender.BehaviorID = this.Name;
        this._hiddenFieldExtender.ID = "xt";
        this._hiddenFieldExtender.TargetControlID = _field.ID;
        this.Controls.Add(this._hiddenFieldExtender);
    }
 
    protected override void OnPreRender(EventArgs e)
    {
        // its time to save the server-side dictionary into the hidden field, which will transport it to the client side component
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        _field.Value = serializer.Serialize(this.Values);
 
        base.OnPreRender(e);
    }
}

 

The comments should make the above code pretty much self-explanatory, so lets just keep rolling and dive right into the javascript code:

Type.registerNamespace('Iridescence.Web.UI');
 
Iridescence.Web.UI.ClientDictionaryBehavior = function(element)
{
    Iridescence.Web.UI.ClientDictionaryBehavior.initializeBase(this, [element]);
    this._dictionary = null;
    this._onSubmitDelegate = null;
}
 
Iridescence.Web.UI.ClientDictionaryBehavior.prototype = 
{
    initialize : function()
    {
        Iridescence.Web.UI.ClientDictionaryBehavior.callBaseMethod(this, 'initialize');
 
        this._onSubmitDelegate = Function.createDelegate(this, this._onSubmit);
        $addHandler(window, "submit", this._onSubmitDelegate);
        this._deserialize();
    },
 
    getValue : function(name)
    {
        var value = this._dictionary[name];
 
        return value;
    },
 
    setValue : function(name, value)
    {        
        this._dictionary[name] = value;
    },
 
    _onSubmit : function()
    {
        // serialize the dictionary to the hidden field    
        this.get_element().value = Sys.Serialization.JavaScriptSerializer.serialize(this._dictionary);
    },
 
    _deserialize : function()
    {
        // deserialize the dictionary from hidden field    
        this._dictionary = Sys.Serialization.JavaScriptSerializer.deserialize(this.get_element().value);          
    },
 
    clear : function()
    {
        this._dictionary = null;
        this._dictionary = new Object();
    },
 
    dispose : function()
    {
        Iridescence.Web.UI.ClientDictionaryBehavior.callBaseMethod(this, 'dispose');  
    }
}
 
Iridescence.Web.UI.ClientDictionaryBehavior.registerClass('Iridescence.Web.UI.ClientDictionaryBehavior', AjaxControlToolkit.BehaviorBase);

 

The only thing left to define is the ClientDictionaryExtender, which is dead simple:

[ClientScriptResource("Iridescence.Web.UI.ClientDictionaryBehavior", "Iridescence.Web.UI.ClientDictionaryBehavior.js")]
[TargetControlType(typeof(HiddenField))]
internal class ClientDictionaryExtender : ExtenderControlBase
{}

 

There’s no server-side functionality in the extender, its job is just to hook up the JavaScript component to the hidden field. I’ve made it internal as well, as it makes little sense to use it without the plumbing being handled by the ClientDictionary control.

And that is all there is to it! Dropping the ClientDictionary component onto a form, I can now set and get values from its dictionary both from the client-side JavaScript and the server-side codebehind, and it will be synchronized both ways.

 

Currently rated 3.8 by 22 people

  • Currently 3.818182/5 Stars.

Comments

2/21/2008 11:31:24 AM

Gj Sonke

Hi! I think you might know tha answet to my question. I don’t really understand the ‘dictionary’ things, I cannot see where exactly the javascript-C# connection is, but my question also concerns sharing of variables between javascript and C#. In Google Maps (javascript) I can get Latitude and Longitude, but now I need these values for in a method in the C# code. How do I get it there?

I hope you can help me.
gjsonke (AT)gmail(DOT)com
Cheers – Gj

Gj Sonke

6/30/2008 7:07:32 PM

Mike

Thank you for this post. I’m looking to solve this very problem although I do not have VS2008 nor Ajax experience. I’m hoping to install Ajax on my dev machine to work with ASP.NET 2.0, could you kindly provide a VS 2005 solution of your sample code?

Thanks for your help

Mike

12/2/2008 11:38:22 AM

Kimi

hi,

im wondering how to call my application variable in my code behind to javascript

ex:

code behind sample.aspx.cs

Application[“myVar”] = “something”;

javascript:

<i want to do this in javascript like in the ex code below but i cant>

if (Application[“myVar”] == “something”) {

alert(“msg”);

}

can you do some code for me? thx.

Kimi United States

3/2/2009 11:23:08 AM

jeffrey

Thanks for your post. It help me a lot. 2 Thumbs up Smile

jeffrey

3/9/2009 10:11:04 AM

Anuj Tripathi

TO pass the value from javascript to server side do this :

Declare a variable (or if already you have a variable) – var a = “this is a demo”;
// now declare a hidden variable which holds the value
var hidden = ‘<%= inpHide.ClientID %>’;
// Now put the variable to the hidden state
document.getElementById(hiddenControl).value=a ;

// access it from the server side

TextBox1.Text = inpHide.Value;

That’s it.

Anuj Tripathi India

3/17/2009 7:08:45 PM

T. Ray Humphrey

Beautiful! All I wanted was to access some ClientIDs in a .js file. No luck!

Then along came you. What a simple, elegant solution. Very clean, very reusable.

Thanks!

T. Ray Humphrey United States

WinGac, a GUI for the Global Assembly Cache

I put together a nice useful tool today that gives gacutil.exe a graphical user interface, making it dead easy to manage the GAC :)

 

In addition to its nice filter functionality (just start typing and the list will start shrinking), you can also select rows and uninstall them from the gac, or use an open file dialogue to install assemblies from disk. Maybe I’ll add more functionality in the future, but as of now it does everything I needed it to do :)
Download WinGac!
Download WinGac with source code!

 

Visste du at, ved å ha en vel-sammensatt webside som er lett å indeksere og navigere i, blir annonse kostnadene billigere i Adwords?

Ja, det stemmer, i tillegg til rimelige CPC, får du også mulighet til å legge til sitelinks (menyer) og rich snippets (Bl.a. stjerner). Så i sin helhet, får dine annonser ekstra synlighet til en lavere pris!

Ikke glem organiske trafikkvolumer

Mange markedsførere har ikke tatt inn over seg hva det betyr at bruken av søkemotorer har fått slik enorm vekst. Mange opplever at søkemotorene kan stå for langt over halvparten av totaltrafikken til et nettsted, og hvis man ser bort fra faste kunder, så er det ikke uvanlig at man kan skaffe 90% av nye kunder til nettstedet gjennom søkemotorer.

Fordi søkemotoroptimalisering er et komplisert felt finnes det ingen standardpakker som passer for alle. Vi anbefaler å starte med en workshop eller en grundig statusanalyse, for å kartlegge hvor skoen trykker, og hvilke tiltak som vil gi størst effekt.

adwords_NEW

Søkemotorer liker relevant informasjon.

Hvis du ønsker å få flere besøkende til din verksted side, vil innhold som tekniske tips, reparasjons guider, videoguider og annen informasjon skyve deg frem i søkeresultatene.

Akkurat som det er forskjell på kvalitet på service er det forskjell på en websides innhold. Takket være vår store interesse til hvordan søkemotorer fungerer, holder vi oss til enhver tid oppdatert med endringer som har en effekt på plassering ved søk.

Sanntidssøk er alltid den beste reklamen for en SEO byrå, og vi har mange kunder å henvise til, der deres søkposisjoner er en live demo av vårt arbeid.

cheese

Vi har en del domener som kan brukes for å øke synligheten for din side, i tillegg til dette kan det kobles opp flere utløpte domener som ikke er lenger i bruk.

La oss gi deg et eksempel:

Du driver et verksted, og har enwebside for denne.

Vi kjøper opp utgåtte verskteds websider, disse kan være bedrifter som har gått konkurs eller glemt å fornye sine domeneleier. Vi kobler så disse til din eksisterende side, da vil alle som besøker de gamle sidene komme direkte til deg, dette vil samtidig øke din relevans som verkstedsside for søkemotorer.

Det blir da som å spørre etter et verksted i Oslo, og de anbefaller heller deg fordi denne er stengt.

 

  • Du tar kontakt med webbyrå og sender informasjon om din bedrift.
  • Din side analyseres mot dine konkurrenter og lignende bransjer.
  • Det tas der etter en møte sammen, enter ved personlig oppmøte eller telefon\epost.
  • Ut i fra dine behov kartlegges hva websiden skal inneholde og hvordan designen skal være.
  • Det legges fram et pristilbud med oversikt og eventuelt support avtalekostnader, dersom dette ønskes.
  • Gjennom utviklingsfasen, får man vanligvis tilgang til en privat nettbasert side, der du kan gi dine tilbakemeldinger.
  • Når siden har blitt grundig testet og godkjent av deg, publiseres denne.

Prestashop er en annen alternativ innen nettbutikk-platformer og ble lansert i 2005.

Med mange år bak seg, og konstant utvikling er dette en platform som tilbyr både stabilitet og fart i ett.

Det som startet som en studentprosjekt vokste til over 100 ansatte over kort tid, og har i dag ca. 30% av nettmarkedet på nett.

I likhet med Magento og Openchart, tilbyr Prestashop klare tema, god utvalg av utvidelser og meget gode sikkerhetsegenskaper.

1 2 3 17