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.
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'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 have a url: like this one: http://www.example/about/49.
I want it to be seen as http://www.example/about/, but I must have this parameters passed as QueryString parameters.
Is it possible ?
Be careful with session variables; it's easy to have multiple pages opened which are all using the same session and end up mixing the values.
It would be better to use TempData, which only allows the value to be used once (removed on first access). However, this implies the value will be used almost immediately.
You can also write a cookie with the desired value, intercept the request (ASP.Net provides a variety of ways of doing this, such as the BeginRequest event), and internally process the URL as though it contained the value.
Of course, you then must cleanup the cookie (which will have the same problem as a Session-based solution). Remember that a cookie is more vulnerable to tampering on the client.
Personally, I think any of these approaches are far more trouble than they are worth. "Hackable URLs" (such as those which contain a potentially meaningful ID) are usually a good thing.
My workaround for this (Which works REALLY well, thanks to the help of the SO Community)
Create a class called SiteSession.cs
Input the following code:
using System;
using System.Collections.Generic;
using System.Web;
/// <summary>
/// Summary description for SiteSession
/// </summary>
public class SiteSession
{
/// <summary>
/// The _site session
/// </summary>
private const string _siteSession = "__SiteSession__";
/// <summary>
/// Prevents a default instance of the <see cref="SiteSession" /> class from being created.
/// </summary>
private SiteSession()
{
}
/// <summary>
/// Gets the current Session
/// </summary>
/// <value>The current.</value>
public static SiteSession Current
{
get
{
SiteSession session = new SiteSession();
try
{
session = HttpContext.Current.Session[_siteSession] as SiteSession;
}
catch(NullReferenceException asp)
{
}
if (session == null)
{
session = new SiteSession();
HttpContext.Current.Session[_siteSession] = session;
}
return session;
}
}
//Session properties
public int PageNumber {get;set;}
}
You can put anything in the Session Properties, just make sure its public.
Then, set it by:
SiteSession.Current.PageNumber = 42
And call it with
int whatever = SiteSession.Current.PageNumber
My question is very similar to this issue: AntiForgery Exception: A required anti-forgery token was not supplied or was invalid
but I have the MVC3 and I using Razor installed.
controller has the
[ValidateAntiForgeryToken]
specified
in html is printed <input name="__RequestVerificationToken"... using #Html.AntiForgeryToken()
Also I observed, that if I remove the Authorization cookie in the browser, and controller method does not have [Authorize] I don't have any problems with AntiForery. Why?
Check your cookies and make sure that you are seeing the requestVerificationToken cookie being set correctly. I have run into this before where the cookies for the site were all set to be SSL only and I was trying to run it over regular HTTP locally, so the cookie was never being accepted because it was being transmitted over unsecure channels.
For me, this meant changing a line in the web.config under system.web/httpCookies to requireSSL="false"... but if this isn't what you are seeing, I would still look at things that might be messing with your cookies in the system (e.g. session resets, manually clearing the cookies somewhere, etc.). If you have the validation attribute on the controller methods correctly, and are still getting this, it is likely due to something modifying or removing that cookie!
Edit: Also, if you have this on the controller instead of only on the POST methods, that would be why... This is only applicable to form POSTs to the server.
Here's a simple custom version that you CAN apply to the form that will automatically validate on ALL POST action methods:
/// <summary>
/// Custom Implementation of the Validate Anti Forgery Token Attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
/// <summary>
/// The ValidateAntiForgeryTokenAttribute.
/// </summary>
private readonly ValidateAntiForgeryTokenAttribute _validator;
/// <summary>
/// The AcceptVerbsAttribute.
/// </summary>
private readonly AcceptVerbsAttribute _verbs;
/// <summary>
/// Initializes a new instance of the <see cref="CustomValidateAntiForgeryTokenAttribute"/> class.
/// </summary>
/// <param name="verbs">The verbs.</param>
public CustomValidateAntiForgeryTokenAttribute(HttpVerbs verbs) : this(verbs, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomValidateAntiForgeryTokenAttribute"/> class.
/// </summary>
/// <param name="verbs">The verbs.</param>
/// <param name="salt">The salt.</param>
public CustomValidateAntiForgeryTokenAttribute(HttpVerbs verbs, string salt)
{
_verbs = new AcceptVerbsAttribute(verbs);
_validator = new ValidateAntiForgeryTokenAttribute
{
Salt = salt
};
}
/// <summary>
/// Called when authorization is required.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public void OnAuthorization(AuthorizationContext filterContext)
{
var httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
var found = false;
foreach (var verb in _verbs.Verbs)
{
if (verb.Equals(httpMethodOverride, StringComparison.OrdinalIgnoreCase))
{
found = true;
}
}
if (found && !filterContext.RequestContext.RouteData.Values["action"].ToString().StartsWith("Json"))
{
_validator.OnAuthorization(filterContext);
}
}
}
Then you can just add the following to all of your controllers, or to your base controller if you override and inherit from one:
[CustomValidateAntiForgeryToken(HttpVerbs.Post)]
Anti forgery token is tied to the user identity. If you changing currently logged in user identity between generating and validating tokens then token will not be validated successfully. Also, that explains why everything is working for you in anonymous mode.