Most CF MVC Frameworks use the front controller pattern. Usually Search Engine Safe (SES) plugin together with URL Rewrite are used to construct friendly URLs. However, when it comes to implementing RESTful services, using a MVC framework seems like a layer of complexity added on top of another layer of complexity.
How should one tame this beast? Any nice and clean approach of supporting RESTful services with ColdFusion? Any MVC framework out there that can expose RESTful services easily?
Thanks
ColdBox has been supporting RESTful URLs for a long time now. In 3.0 you can even split the incoming HTTP verbs to execute different actions in a nice decoupled manner. read here: http://blog.coldbox.org/post.cfm/coldbox-rest-enabled-urls
You can even have HTTP method security on your event handlers very easily:
component{
this.allowedMethods = {
LIST = "GET",
SAVE = "POST,PUT"
};
}
I have been using Powernap (http://powernap.riaforge.com) to implement RESTful web services. It's not an MVC framework, but I think it could work alongside whatever framework you're currently using in your app.
I tried using PowerNap a while ago, but I felt it didn't fit well with what I was doing (building an API on top of an existing application). My solution was RESTfulCF: it's front-controller, but doesn't implement full MVC, because (as you say) that's overkill.
We're currently using RESTfulCF to power a number of (heavy use) internal systems at White Label Dating, and it's running like a dream while allowing us to continue building the rest of the application separately from the API layer, which we use to expose just the systems we need.
Funny you should ask. I am a fan of PowerNap, but I thought it could be done a little better another way, so I started my own framework last week. It's still a front-controller framework so everything is channeled through index.cfm (which is easily removed using url-rewriting), but it's built specifically for writing RESTful web services. It draws a lot of inspiration from PowerNap as well as FW/1.
It's still kind of rough, but it works. Right now I'd call it a proof of concept; but it doesn't have far to go before I call it version 1.0. I've put some information and the source on github.
Update 8/23/2010: Now officially at 1.0! :)
I use MVC as per Fowler's PageController pattern to implement REST services. One controller per resource and the controller implements a method for each of the http methods supported. i.e. GET, PUT, POST, DELETE.
Works well for me. The only area where my approach differs from the standard interpretation of MVC is that my model is really a model of the UI content. It's not a domain model. It may contain elements from the domain model, but it may also contain other content.
Quicksilver is not bad! http://quicksilver.riaforge.org/
/**
* #url /hello/{text}
* #httpMethod GET
*/
public String function saySomething(required String text) {
return "Hello " & arguments.text;
}
Actual URL:
index.cfm/hello/developer
Another option is Taffy (https://github.com/atuttle/Taffy). Add one CFC per URI template, and define a method for each HTTP method you want to support (GET, PUT, DELETE, etc).
<cfcomponent extends="taffy.core.resource" output="false"
taffy_uri="/user/{userID}/stuff/{stuffID}/property/{propertyID}">
<cffunction name="get" access="public" output="false">
...
</cffunction>
<cffunction name="post" access="public" output="false">
...
</cffunction>
<cffunction name="delete" access="public" output="false">
...
</cffunction>
</cfcomponent>
Related
I recently upgraded to ABP 4.10.1 and every method in AppServices were now suffixed by Async, I think it's great news for us backend developers but it's not so great for the frontend developers / external users of the API -> the Swagger documentation feels less easy to read :
every Create became CreateAsync
every Update became UpdateAsync
...
I think the "Async" is really not needed. Is there a way to act on the CreateControllersForAppServices to tell it to remove any trailing Async when creating the Web API methods while keeping the AppService naming for backend developers?
Thanks in advance.
One way is to use ActionName attribute if you are willing to add this to every method.
[ActionName("Create")]
public string CreateAsync()
{
return "Hello from Create Method";
}
I've got a webAPI that uses Entity Framework. I'm trying to cache some data in the session variable following along in this article:
https://msdn.microsoft.com/en-us/library/system.web.httpcontext.session(v=vs.110).aspx
I can't seem to do it though. The Session object isn't available.
In my controller, I try this:
Session["mappings"] = mappings;
...but it doesn't recognize what Session is.
I also try this:
HttpContext.Current.Session["mappings"] = mappings;
...and this:
Page.Session["mappings"] = mappings;
...but it doesn't know what HttpContext or Page are.
I'm including System.Web in my project references. I'm also including this in my web.config:
...just like this article says:
https://msdn.microsoft.com/en-us/library/ms178581(v=vs.110).aspx
...but to no avail.
My work colleague suggests it's because our webAPI is RESTful which means it's stateless, so no session object. However, we know there are ways around this. What I need is simply some way of persisting data in some kind of cache that will survive across several requests.
I also need something that will be available inside EF entities (not just the webAPI controller) is that's possible.
Does anyone know how to solve this problem? Thanks.
As your colleagues correctly suggested, an API is stateless, each request is separate and needs to have all the data required to complete the request.
You can add a caching layer however, but that is not going to be done via the Session object. Session makes no sense in an API.
Have a look here for some ideas: Caching Data in Web API
This article suggests it was possible, or in the works, with some code that suggests it can be done, but I can't figure out what code needs to happen or the WcfTestClient's uri needs to be.
Here's the code from the article that makes me think I can do it:
// Metadata routes to support $metadata and code generation in the WCF Data Service client.
configuration.Routes.MapHttpRoute(
ODataRouteNames.Metadata,
"$metadata",
new { Controller = "ODataMetadata", Action = "GetMetadata" }
);
Is this feature implemented?
No, it does not work as you intend. WCF Test Client supports talking to SOAP-based services. OData is not supported in the current version.
Granted, as #Snixtor mentioned, you could create a SOAP service using ASP.NET Web API, including support for metadata (WSDL). But I really don't know of any good reason why anyone would want to do that.
Is there a built in function which will test if an email address is valid?
I want to test the email address structure is valid before sending a confirmation email to the end user.
I understand i could create my own function easy enough with the use of a regular expression but if there is a built in function i would much rather use this.
You can do this with Data Annotations extensions I believe. Check out Scott Guthrie's blog post on it here: http://weblogs.asp.net/srkirkland/archive/2011/02/23/introducing-data-annotations-extensions.aspx.
There is a good point in Scott's post as to why you would use this rather than the MVC 3 Futures validators which might be relevant to your choice:
ASP.NET MVC 3 futures defines four new data annotations attributes which this project has as well: CreditCard, Email, Url and EqualTo. Unfortunately referencing MVC 3 futures necessitates taking an dependency on MVC 3 in your model layer, which may be unadvisable in a multi-tiered project. Data Annotations Extensions keeps the server and client side libraries separate so using the project’s validation attributes don’t require you to take any additional dependencies in your model layer which still allowing for the rich client validation experience if you are using MVC 3.
Yes, you can use
public class CustomerMetaData
{
// Add type information.
[DataType(DataType.EmailAddress)]
public object EmailAddress;
}
on your model. See more about it here.
however, last time I checked it does not work client sided.
I googled it, and from imran baloch's blog post it seems it does work now.
So... I have a business object/manager which is going to generate emails.
These emails will contain links to various content on the website... and therefore needs to understand about MVC routing.. or at least how to generate URLs for the website...
However my business object will not have access to a RequestContext etc and the email generation is not necessarily the result of a web request to a website (I have a dispatcher which runs on a background thread which will be generating the emails)
Any ideas how I can generate my urls without having access to a request - and therefore being unable to use URLHelper...
Thoughts?
In order to get at the UrlHelper outside of the controller, you need to feed it and the routing data the HttpContext. Here's an example:
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
HttpContextBase context = new HttpContextWrapper(HttpContext.Current);
UrlHelper helper = = new UrlHelper(new RequestContext(context, RouteTable.Routes.GetRouteData(context)));
I prefer to define schema and make both routing and business logic aware of it. Means different implementations of the same URL schema.
Some reasons why:
Your routing mechanism could change. For example in feature you can switch to url_rewrite module.
Possible issues with load-balanced installation.
You do not need even to try to use URLHelper in undocumented way.
BTW, you can replace HttpRequest from URLHelper easily. We used to use this for unit-testing. For more information just search for unit testing of the HttpContextBase or look at examples in source code of the MvcContrib. This can help to instantiate URL helper and all related stuff in non hosted environment. But I still do not think that this is a good idea.
In ASP.NET MVC5 (and possibly MVC4 - I'm not sure when it was introduced), you can do this more directly using HttpRequest.RequestContext. Eg:
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);