Im having a little trouble about extracting some data from Expression<Func<,>>, and having more trouble explaining what i want.
So i have a rest API and im building a method that could work as WFC.
meaning that i will not be needing to call the rest api by url but.
Building an interface or some kind of wrapper and from that interface we could extract the rest api controller Name, method and parameters.
Ok let me show you how i have imagine it to work.
This is my controller interface
[Route(url: "api/")]
public interface IYoutubeController
{
/// <summary>
/// Get a collection of the searched youtube videos
/// </summary>
/// <param name="searchString"></param>
/// <param name="pageSize"></param>
/// <param name="relatedTo"></param>
/// <param name="videoSearchType"></param>
/// <returns></returns>
[Route]
YoutubeVideoCollection Search(string searchString, int pageSize = 50, string relatedTo = null, VideoSearchType videoSearchType = VideoSearchType.Videos);
/// <summary>
/// Get the playlist video contents
/// </summary>
/// <param name="playListId"></param>
/// <returns></returns>
[Route]
List<YoutubeItem> Playlist(string playlistId);
/// <summary>
/// decrypted youtube video
/// </summary>
/// <param name="videoId"></param>
/// <returns></returns>
[Route(httpMethod: HttpMethod.POST)]
Task<YoutubeVideoInfo> GetVideoAsync(string videoId);
}
This the controller Repository
public static class ControllerRepository
{
public static async Task<P> Execute<P>(Expression<Func<IYoutubeController, P>> func)
{
return await HttpHelper.ExecuteAsync(func);
}
}
Now i could simple call my method like this
YoutubeVideoCollection test = await ControllerRepository.Execute(x => x.Search("Eminem"));
You can se that there is no method seach exist, its only a method in interface.
And here is a httphelper, in it exist PostAsync and GetAsync and also ExecuteAsync
private static string baseUrl = "http://xxx"
public static async Task<P> ExecuteAsync<T, P>(Expression<Func<IYoutubeController, P>> func)
{
var url= typeof(T).GetCustomAttribute<Route>()?.Url ?? ""; // eg api/
var controller = typeof(T).Name; // eg IYoutubeContrller, will be renamed to youtube later on
var method = // get the method from func P which is Search
var parameters = // the parameter data from func which is Eminem
var fullUrl= $"{baseUrl}/{url}/{controller}"
// and here we do PostAsync<P>(fullUrl, parameters ) Or GetAsync<P>(fullUrl, parameters )
}
In HttpHelper ExecuteAsync i want to retrive from my Func<T, P> the Method name which is Search and the parameter which is searchString and also the value of the parameter which is Eminem
Could you help me retrive these information from func parameter?
This is still an idee, so it may not really work, just let me know if its possible.
For your very special case:
Execute(x => x.Search("Eminem"));
You could do this
public static async Task<P> ExecuteAsync<T, P>(Expression<Func<IYoutubeController, P>> func)
{
MethodCallExpression callExpression = expression.Body as MethodCallExpression;
string methodName = callExpression.Method.Name;
object argument = ((ConstantExpression)callExpression.Arguments).Value;
// do something
}
But of course this will crash if the expression passed to Execute is more complicated or uses calls with no arguments or non-constant expression arguments etc.
But in that case, you wouldn't know which information to extract at all.
Related
I'm currently struggling to write a really good SDK for our API. I'm looking at doing it one of a few ways right now.
I can either pass the HttpResponseMessage back from my API call (as you can see below).
I can do some data processing and pass back just the object.
I can create a custom object to wrap the SDK return so that it can contain what it needs to.
I can throw exceptions when a server error is encountered, but that can be expensive especially if the application can recover from the exception.
I could also return a tuple in the getter here so that I get both an object and the HttpResponseMessage.
I want the SDK to do just the right amount of processing on the data and I'm not sure if there is an example of how to write a good SDK in .NET or not. I am actually going to consume this SDK myself so I want it to be good. I have written some code and I'll include that here but I think it's current iteration is flawed.
public interface IBaseApi<T>
{
Task<IEnumerable<T>> GetAllAsync();
Task<T> GetByIdAsync(int id);
Task<HttpResponseMessage> InsertAsync(T obj);
Task<HttpResponseMessage> UpdateAsync(T obj);
Task<HttpResponseMessage> DeleteAsync(int id);
}
Right now we return a null object to indicate that either Get request failed. But I think that's a flawed concept.
I've also looked at the Facebook, Square, and a few other Sdks. Nothing quite hit the mark for me.
So what return type should my API be sending? Just a pointer, I don't know how the client is going to consume this. It could be part of a larger query or a direct pass through to their Controller. My overall goal is that the consumer will have as little processing to do themselves, but also a meaningful if something goes wrong message.
What I have done in the past and has worked really well is to create an "Api Response" object that contains metadata about the response as well as the actual data resulting from the call, something along the lines of:
public class ApiResponse<TData>
{
/// <summary>
/// Constructor for success.
/// </summary>
/// <param name="data"></param>
public ApiResponse(TData data)
{
Data = data;
Success = true;
Errors = new List<string>();
}
/// <summary>
/// Constructor for failure.
/// </summary>
/// <param name="ex"></param>
public ApiResponse(IEnumerable<string> errors)
{
Errors = errors;
Success = false;
}
/// <summary>
/// Gets whether the API call was successful.
/// </summary>
public bool Success { get; private set; }
/// <summary>
/// Gets any errors encountered if the call was not successful.
/// </summary>
public IEnumerable<string> Errors { get; private set; }
/// <summary>
/// Gets the data resulting from the API call.
/// </summary>
public TData Data { get; private set; }
}
You could have a base class that does not return any data too and then derive this one from that.
I am trying to get my Dialog to work with my database.
If I have my dialog like this:
[Serializable]
public class QuestionDialog : IDialog<object>
{
/// <summary>
/// Start our response
/// </summary>
/// <param name="context">The current context</param>
/// <returns></returns>
public async Task StartAsync(IDialogContext context)
{
// Move to the next method
context.Wait(StepOneAsync);
}
/// <summary>
/// When our message is recieved we execute this delegate
/// </summary>
/// <param name="context">The current context</param>
/// <param name="result">The result object</param>
/// <returns></returns>
private async Task StepOneAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
// Get our activity
var activity = await result;
// Ask our first question
await context.PostAsync("hi");
// Get our answer
context.Done(this);
}
}
Everything works fine and I get my message as expected. I then changed it to this:
[Serializable]
public class QuestionDialog : IDialog<object>
{
// Private properties
private IList<QuestionGroup> _questionGroups;
/// <summary>
/// Start our response
/// </summary>
/// <param name="context">The current context</param>
/// <returns></returns>
public async Task StartAsync(IDialogContext context)
{
try
{
// Create our service
var questionGroupService = new QuestionGroupService(new UnitOfWork<DatabaseContext>());
// Add our question groups
this._questionGroups = await questionGroupService.ListAllAsync();
// Move to the next method
context.Wait(StepOneAsync);
} catch (Exception ex)
{
}
}
/// <summary>
/// When our message is recieved we execute this delegate
/// </summary>
/// <param name="context">The current context</param>
/// <param name="result">The result object</param>
/// <returns></returns>
private async Task StepOneAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
// Get our activity
var activity = await result;
// Ask our first question
await context.PostAsync("hi");
// Get our answer
context.Done(this);
}
}
And it doesn't go the the StepOneAsync method. Can anyone see anything glaringly obvious as to why this isn't working?
Make sure your QestionGroup model is marked as Serializable.
If you cannot make it serializable and you still want to reference it during your dialog, you need to go with one of the alternatives described in the "How can I reference non-serializable services from my C# dialogs?" section of the Bot Framework Technical FAQ.
The simplest one is to use the NonSerialized attribute in your field.
Alternatively, you can try using registering the Reflection Serialization Surrogate by adding it to the Autofac container. In your global.asax, try adding this code:
var builder = new ContainerBuilder();
builder.RegisterModule(new ReflectionSurrogateModule());
builder.Update(Conversation.Container);
I've been doing some searching and can't quite find what I'm after.
I'm working on an Umbraco project and need to render the contents of a specific page inside a parent page.
I tried
#Html.Action("MyAction", "MyController")
but this caused the assigned content item of my page to be null (because I've gone to the action rather than going to the umbraco url) which isn't really a big surprise.
Is there a Html method like Action that accepts a url instead of an action name and controller name?
I'm after something like this
#Html.LoadUrl("~/my-controller-section/my-action-page")
I could write an extension myself to do it, but was really hoping that there would already be something built into MVC to do this. I don't want to re-invent the wheel.
I'm using ASP.Net MVC 4.
OK, so it seems that there isn't a way to do this in MVC so I've reluctantly rolled my own. If a built in way of doing this comes to light I'll swap it out.
I've implemented the LoadUrl method that I proposed in my question, it's consumed like this:
#Html.LoadUrl("http://foo/bar")
The implementation looks like this (sorry it's a bit long, but I wanted to include a full working example so you can just copy and paste it).
You'll notice that I set the authentication cookie on the request, otherwise it'll just return the login page instead of the requested page. I've specifically implemented this for Forms Authentication. This part isn't required if your site doesn't use authentication.
public static class MvcExtensions
{
/// <summary>
/// Invokes a request to the specified url and returns the result as an HTML string.
/// </summary>
/// <param name="thisHtml">The HTML helper instance that this method extends.</param>
/// <param name="url">The url to invoke the request on.</param>
/// <returns>The url contents as an HTML string.</returns>
public static MvcHtmlString LoadUrl(this HtmlHelper thisHtml, string url)
{
//get the url as an absolute url
UrlHelper helper = GetUrlHelper();
url = helper.Absolute(url);
var request = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
//set the auth cookie so we don't just get the login page
request.SetAuthenticationCookie();
//get the response
string responseString = request.GetResponseString();
//return it as an html string
return new MvcHtmlString(responseString);
}
private static UrlHelper GetUrlHelper()
{
return new UrlHelper(HttpContext.Current.Request.RequestContext);
}
/// <summary>
/// Gets an absolute version of the specified url relative to the current requests url root.
/// </summary>
/// <param name="thisHelper">The Url helper instance that this method extends.</param>
/// <param name="url">The url to get an absolute version of.</param>
/// <returns>An absolute version of the specified url relative to the current requests url root.</returns>
public static string Absolute(this UrlHelper thisHelper, string url)
{
//only make the url absolute if it isn't already
if (!url.Contains("://"))
{
return string.Format("http://{0}{1}", thisHelper.RequestContext.HttpContext.Request.Url.Authority, thisHelper.Content(url));
}
else
{
return url;
}
}
/// <summary>
/// Sets the authentication cookie of the specified request.
/// </summary>
/// <param name="request">The request to set the cookie for.</param>
/// <param name="authenticationCookie">(Optional) The cookie to add to the request, if not specified defaults
/// to the cookie of the user currently logged into the site.</param>
public static void SetAuthenticationCookie(this HttpWebRequest request, Cookie authenticationCookie = null)
{
if (authenticationCookie == null)
{
//add the current authentication cookie to the request
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
authenticationCookie = new System.Net.Cookie
(
FormsAuthentication.FormsCookieName,
cookie.Value,
cookie.Path,
FormsAuthentication.CookieDomain
);
}
request.CookieContainer = new System.Net.CookieContainer();
request.CookieContainer.Add(authenticationCookie);
}
/// <summary>
/// Gets the response string from the specified request.
/// </summary>
/// <param name="request">The request to get the response string from.</param>
/// <returns>The response string from the specified request.</returns>
public static string GetResponseString(this System.Net.WebRequest request)
{
System.Net.WebResponse response = null;
try
{
response = request.GetResponse();
}
catch (System.Net.WebException webException)
{
response = webException.Response;
throw;
}
return GetResponseString(response);
}
/// <summary>
/// Gets the response string from the specified response.
/// </summary>
/// <param name="response">The response to get the response string from.</param>
/// <returns>The response string from the specified response.</returns>
public static string GetResponseString(this System.Net.WebResponse response)
{
using (var responseTextStream = new System.IO.StreamReader(response.GetResponseStream()))
{
return responseTextStream.ReadToEnd();
}
}
}
Using .NET 4.5.1, Web API 2, Visual Studio 2013:
I have a Web API which has the following routes...
/api/providers/specialties
/api/providers/specialties/123
/api/providers/specialties/medicine
These work as expected... the first one gets a list of all specialties, the second one gets specialty ID 123, and the third gets all specialties with "medicine" in the name.
I also have these routes...
/api/locations/specialties
/api/locations/specialties/123
/api/locations/specialties/ortho
Only the last two work... the first one returns this error:
No HTTP resource was found that matches the request URI [...]
No route providing a controller name was found to match request URI 'http://mysite/api/locations/specialties'
How can this be? It will hit other routes in that controller, just not the base one.
(I also have two other controllers with the routes /api/providers and /api/locations by themselves, which work fine.)
Here is the ProviderSpecialtyController.cs code:
[RoutePrefix("api/providers/specialties")]
public class ProviderSpecialtyController : ApiController
{
private ProviderEntities db = new ProviderEntities();
/// <summary>
/// Get ALL specialties, sorted by name.
/// </summary>
[Route("")]
public IQueryable<ProviderSpecialty> Get()
{
return db.ProviderSpecialties.OrderBy(s => s.Name);
}
/// <summary>
/// Get a specific specialty.
/// </summary>
/// <param name="id">The ID of a particular specialty.</param>
[Route("{id:int}")]
public ProviderSpecialty Get(int id)
{
return db.ProviderSpecialties.Where(s => s.Id == id).FirstOrDefault();
}
/// <summary>
/// Get all specialties that contain a keyword.
/// </summary>
/// <param name="keyword">The keyword to search for in a specialty name.</param>
[Route("{keyword:alpha}")]
public IQueryable<ProviderSpecialty> Get(string keyword)
{
return db.ProviderSpecialties.Where(s => s.Name.Contains(keyword)).OrderBy(s => s.Name);
}
}
And here is the LocationSpecialtyController.cs code:
[RoutePrefix("api/locations/specialties")]
public class LocationSpecialtyController : ApiController
{
private ProviderEntities db = new ProviderEntities();
/// <summary>
/// Get ALL specialties, sorted by name.
/// </summary>
[Route("")]
public IQueryable<LocationSpecialty> Get()
{
return db.LocationSpecialties.OrderBy(s => s.Name);
}
/// <summary>
/// Get a specific specialty.
/// </summary>
/// <param name="id">The ID of a particular specialty.</param>
[Route("{id:int}")]
public LocationSpecialty Get(int id)
{
return db.LocationSpecialties.Where(s => s.Id == id).FirstOrDefault();
}
/// <summary>
/// Get all specialties that contain a keyword.
/// </summary>
/// <param name="keyword">The keyword to search for in a specialty name.</param>
[Route("{keyword:alpha}")]
public IQueryable<LocationSpecialty> Get(string keyword)
{
return db.LocationSpecialties.Where(s => s.Name.Contains(keyword)).OrderBy(s => s.Name);
}
}
As you can see, they are nearly identical except for the route prefix. Why does the provider controller work as expected but location controller does not?
I have enabled tracing and the following is observed when trying to hit /api/locations/specialties:
System.Web.Http.Request: GET http://localhost:49565/api/locations/specialties/: Category=System.Web.Http.Request, Level=Info Begin http://localhost:49565/api/locations/specialties/
System.Web.Http.Controllers: GET http://localhost:49565/api/locations/specialties/: Category=System.Web.Http.Controllers, Level=Info Begin DefaultHttpControllerSelector SelectController Route='MS_SubRoutes:System.Web.Http.Routing.IHttpRouteData[]'
[...]
System.Web.Http.Controllers: GET http://localhost:49565/api/locations/specialties/: Category=System.Web.Http.Controllers, Level=Error End DefaultHttpControllerSelector SelectController Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
This was simpler than it seemed, but determining why was made more difficult by poor debugging (which was filed and verified as a bug in Codeplex by Kiran Challa. This should be fixed as of Web API 2.1.
I had a controller with this route:
/api/locations/keyword
Which would do a keyword search on keyword.
I had another controller with these routes:
/api/locations/specialties
/api/locations/specialties/123
/api/locations/specialties/keyword
The API engine was confused, because I had two controllers with essentially the same route. I removed one and the problem was fixed.
According to the Codeplex issue tracker, the issue was verified, closed and a new error message was added in Web API 2.1.
I've got this extension method that check for some info in the request cookie and if this info I'm looking for is not in the cookie I go to the database.
All this is OK but when I built this extension I designed it in my MVC4 application, and now I am refactoring items to relative library's and I cannot seem to find a way to import or add a reference to my class library that would allow me to throw an HttpResponseException.
Any ideas on how to overcome this?
namespace LinkedIn.Extensions
{
public static class httprequest_linkedincontext
{
/// <summary>
/// Extension that creates a OAuthLinkedIn object by checking the cookie first, and if the cookie has no LinkedIn info or has expried then builds the LinkedIn OAuthLinkedIn from the database.
/// </summary>
/// <param name="request"></param>
/// <param name="userName"></param>
/// <returns>oAuthLinkedIn information</returns>
public static oAuthLinkedIn GetLinkedInContext(this HttpRequestMessage request, string userName)
{
OAuthHelper helper = new OAuthHelper(request, userName, false);
oAuthLinkedIn oauth = new oAuthLinkedIn(helper.Verify, helper.Token, helper.TokenSecret);
if (string.IsNullOrEmpty(oauth.Token) || string.IsNullOrEmpty(oauth.TokenSecret))
{
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound));
}
return oauth;
}
}
}
Right now there is a little red squiggle line under Http in the System.Web.Http.HttpResponseException.