I am developing an application that presents a company's twitter feed on a Facebook application. This is a large company with lots of traffic to the FB App so I need to cache the Twitter data I receive (from the twitter API) so that I can avoid their rate limits.
In my code, I use LinqToTwitter to call the API and then I construct a string of JSON with the results. That string is then fed to the user's browser via AJAX.
The rate limit for Twitter API calls is 150 per hour, so I figure I will just place the string of JSON data I construct in a cache object and only refresh it once per minute leaving me well below the Twitter rate limit.
The problem is that I am fairly new to MVC for .NET and can't seem to use System.Web.Caching like I could do in a webforms application.
In older webforms apps I simply did something like:
private string KeyTwitterData;
...
string data = null;
if (Cache[KeyTwitterData] == null)
{
var url = LinqToTwitter.Request.Url;
data = ServiceMethods.GetConversation(url);
Cache.Insert(KeyTwitterData, data);
}
else
{
data = (string)Cache[KeyTwitterData];
}
Can someone please tell me how to accomplish this in MVC3?
Thanks!
Matt
In ASP.NET MVC 3 if you want to cache the result of a controller action you could decorate it with the [OutputCache] attribute:
[OutputCache(Duration = 3600, Location = OutputCacheLocation.Server, VaryByParam = "none")]
public ActionResult Foo()
{
var model = SomeExpensiveOperationToFetchData();
return View(model);
}
If you don't want to cache the entire output of a controller action you could use the MemoryCache class:
var data = MemoryCache.Default[KeyTwitterData];
if (data == null)
{
data = SomeExpensiveOperationToFetchData();
MemoryCache.Default.Add(KeyTwitterData, data, DateTime.Now.AddMinutes(5));
}
// use the data variable here
Use HttpContext.Cache in your controller
string data = null;
if (HttpContext.Cache[KeyTwitterData] == null)
{
var url = LinqToTwitter.Request.Url;
data = ServiceMethods.GetConversation(url);
HttpContext.Cache.Insert(KeyTwitterData, data);
}
else
{
data = (string)HttpContext.Cache[KeyTwitterData];
}
Related
I want to add 3D secure authentication to credit card payments taken through a website. I am using Sitefinity 8, the e-commerce plug-in and SagePay as the payment processor.
I have created a custom payment provider and can successfully redirect users to the 3D secure page. I am able to perform the second authentication call to SagePay using the SagePay integration kit (i.e. externally from the e-commerce plugin). However, I am struggling to find a way to complete the payment due the way the internal e-commerce classes function.
The difficulty is that the order processor treats the payment as declined if 3D secure authentication is required, but there does not seem to be a way to process the order correctly without using the inbuilt functionality. From my inspections of the ecommerce libraries, there seems to be no way to extend or modify these classes due to internal modifiers and concrete implementations.
How can I process the order once I have completed authentiation? Has anyone successfully implemented 3D secure with ecommerce? Or know if it is possible?
This is my custom payment provider at the moment.
public class CustomSagePayProvider : SagePayProvider
{
// Rest of code...
protected override IPaymentResponse ParseReponse(string uniqueTransactionCode, string responseXml)
{
var paymentResponse = base.ParseReponse(uniqueTransactionCode, responseXml);
if (Requires3DSecure(paymentResponse))
{
var responseFields = GetResponseAsDictionary(responseXml);
Set3DSecureFields(responseFields, paymentResponse);
}
return paymentResponse;
}
private bool Requires3DSecure(IPaymentResponse paymentResponse)
{
return paymentResponse.GatewayCSCResponse == "OK";
}
private void Set3DSecureFields(Dictionary<string, string> responseFields, IPaymentResponse paymentResponse)
{
var postValues = new NameValueCollection();
postValues.Add("MD", responseFields.ContainsKey("MD") ? responseFields["MD"] : string.Empty);
postValues.Add("PAReq", responseFields.ContainsKey("PAReq") ? responseFields["PAReq"] : string.Empty);
paymentResponse.GatewayRedirectUrlPostValues = postValues;
paymentResponse.GatewayRedirectUrl = responseFields.ContainsKey("ACSURL") ? responseFields["ACSURL"] : string.Empty;
}
}
And this is the 3D secure payment process using the .NET SagePay integration kit
using SagePay.IntegrationKit;
using SagePay.IntegrationKit.Messages;
// Rest of code
var sagePay = new SagePayIntegration();
IThreeDAuthRequest request = new DataObject();
request.Md = Request.Form["MD"];
request.PaRes = Request.Form["PaRes"];
sagePay.RequestQueryString = sagePay.BuildQueryString(request, ProtocolMessage.THREE_D_AUTH_REQUEST, ProtocolVersion.V_223);
sagePay.ResponseQueryString = sagePay.ProcessWebRequestToSagePay("https://test.sagepay.com/gateway/service/direct3dcallback.vsp", sagePay.RequestQueryString);
var result = sagePay.GetDirectPaymentResult(sagePay.ResponseQueryString);
if (result.Status == ResponseStatus.OK)
{
// Process order
}
I was able to add 3D secure authentication by treating the 2nd authentication call as an offsite payment and adding the IOffsitePaymentProcessorProvider interface to my payment provider class
public class CustomSagePayProvider : SagePayProvider, IOffsitePaymentProcessorProvider
{
// Triggered after payments that have been 3D Secure authenticated
public IPaymentResponse HandleOffsiteNotification(int orderNumber, HttpRequest request, PaymentMethod paymentMethod)
{
var paymentResponse = new PaymentResponse() { IsOffsitePayment = true };
var sagePay = new SagePayIntegration();
var result = sagePay.GetDirectPaymentResult(request.Params.ToString());
if (result.ThreeDSecureStatus == ThreeDSecureStatus.OK)
{
paymentResponse.IsSuccess = true;
paymentResponse.GatewayTransactionID = result.TxAuthNo.ToString();
}
return paymentResponse;
}
public IPaymentResponse HandleOffsiteReturn(int orderNumber, HttpRequest request, PaymentMethod paymentMethod)
{
throw new NotImplementedException();
}
I pass the notification url as a query string parameter in the termUrl value posted to SagePay when first requesting the authentication
(The url must be /Ecommerce/offsite-payment-notification/ to use the inbuilt offside payment notification handler).
var notificationUrl = request.Url.GetLeftPart(UriPartial.Authority) + "/Ecommerce/offsite-payment-notification/";
In the callback from SagePay after the user completes authentication, I use the SagePay integration kit to process the result of the authentication.
var sagePay = new SagePayIntegration();
IThreeDAuthRequest request = new DataObject();
request.Md = md;
request.PaRes = paRes;
sagePay.RequestQueryString = sagePay.BuildQueryString(request, ProtocolMessage.THREE_D_AUTH_REQUEST, ProtocolVersion.V_223);
sagePay.ResponseQueryString = sagePay.ProcessWebRequestToSagePay("https://test.sagepay.com/gateway/service/direct3dcallback.vsp", sagePay.RequestQueryString);
return sagePay.GetDirectPaymentResult(sagePay.ResponseQueryString);
Finally, I trigger the HandleOffsiteNotification event by posting to the url www.mysite.com/Ecommerce/offsite-payment-notification/. This marks the order as complete, updates stock levels and cleans up the user's basket. For simplicity in this example, I am using the SagePay integration kit object to build the query string and post to the url.
var sagePay = new SagePayIntegration();
var ordersManager = OrdersManager.GetManager();
var query = sagePay.ConvertSagePayMessageToNameValueCollection(ProtocolMessage.DIRECT_PAYMENT_RESULT, typeof(IDirectPaymentResult), result, ProtocolVersion.V_223);
// Required Sitefinity fields to trigger HandleOffsiteNotification in IOffsitePaymentProcessorProvider
query.Add("invoice", orderNumber.ToString());
query.Add("provider", ordersManager.Provider.Name);
var queryString = sagePay.BuildQueryString(query);
// Post 3d secure details to this site simulate an offsite payment processor response
sagePay.ProcessWebRequestToSagePay(notificationUrl, queryString);
How can I redirect a user from one Asp.net Mvc site to another Asp.net MVC site and automatically log them in?
The situation is that we will have some customers that need to go to one site and some that will need to go to the other. I've been asked to make it so that when customers are redirected to the correct site that they are also auto logged in to the site they are redirected to.
Assuming you don't want to integrate existing single sign-on solution and that you are using forms authentication for both sites and those sites are not on the same domain. The forms authentication in MVC is done via cookie. So the task is when you're logged in to Site1 to create authentication cookie on Site2.
Usually you craft a request to Site2 like:
/Impersonate/Start?encryptedToken=some_encrypted_stuff
And Site2 handling it like:
[DataContract]
public class Token
{
[DataMember(Name = "u")]
public string UserName { get; set; }
[DataMember(Name = "t")]
public DateTime TimeStamp { get; set; }
[DataMember(Name = "m")]
public string Magic { get; set; }
public Token()
{
Magic = MAGIC;
TimeStamp = DateTime.Now;
}
public const string MAGIC = "SOME_RANDOM_STRING";
}
public class ImpersonateController : Controller
{
[HttpGet]
public ActionResult Start(string encryptedToken)
{
// symmetric encryption - hopefully you know how to do it :)
string decryptedToken = Decrypt(encryptedToken);
var serializer = new DataContractJsonSerializer(typeof(Token));
Token token;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(decryptedToken)))
{
token = serializer.ReadObject(stream);
}
if (!string.Equals(token.Magic, Token.MAGIC) ||
(DateTime.Now - token.TimeStap).TotalMinutes > 1))
{
// magic doesn't match or timestamp is too old
throw new Exception("Invalid token.");
}
FormsAuthentication.SetAuthCookie(token.UserName, true);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}
Maybe needless to say /Impersonate/Start should be under https.
As for crafting the request - you can put it directly into view & make request via json.
In Site1:
public class LoginController : Controller
{
public ActionResult Login(string userName, string password)
{
// ... validate user logs to site 1 etc
var site2UserName = userName;
var token = new Token { UserName = site2UserName };
var serializer = new DataContractJsonSerializer(typeof(Token));
string decryptedToken;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, token);
decryptedToken = Encoding.UTF8.GetString(stream.ToArray());
}
// symmetrical encryption
return new View(new LoginMode { Token = HttpUtility.UrlEncode(Encrypt(decryptedToken)) });
}
}
View (assuming you have jQuery)
$(function() {
$.get("https://site2.com/Impersonate/Start?token=#Html.Raw(Model.Token)");
});
So right after you log-in to Site1 you serve view that uses AJAX to send request to Site2 that will authenticate user also there. It is usually better idea to do it on request - the form authentication cookie for Site2 will eventually expire. So I'd favor something like like this on Site1:
Continue to site 2
And
[HttpGet]
[Authorize]
public ActionResult StartImpersonation()
{
// this is essentially similar to Login action
string encryptedToken = "";
string redirectUrl = string.Format(CultureInfo.InvariantCulture,
"https://site2.com/Impersonate/Start?encryptedToken={0}",
HttpUtility.UrlEncode(encryptedToken));
return Redirect(redirectUrl);
}
Which is better because a) cookie on Site2 can't expire b) if there is an error in impersonation user will see why (if there is an error in AJAX impersonation you can show some error to user, but it will look weird - authentication to site 2 haven't succeeded - why they're trying to authenticate me there ? :).
You want a single-sign-on (SSO) solution. This may be done any number of ways. OpenID is popular: https://en.wikipedia.org/wiki/OpenID This goes into plenty of details on a slightly older approach: http://www.codeproject.com/Articles/114484/Single-Sign-On-SSO-for-cross-domain-ASP-NET-appl Even more stuff here: C# ASP.NET Single Sign-On Implementation
HTH
On the link that would take someone from one site to the other, here's some of what you could put in the JS in a few places that may do what you want:
On clicking the link, a $.get is done to grab the HTML of the log-in page.
Then put into JS variables the login and password of the user for the second site's security into that HTML.
Post the data through a $.ajax request so that the person is logged in through the JS behind the scenes.
Now, either redirect in the current window or open a new window with the other site's home page and voila they are signed in without having to do any extra lifting if their own.
Note that some of this could be done earlier if you want to make the transition easier as when the page with the link loads, this could be done in the JS when the document is ready. The key point here is to have the cookies required for the authentication on the second site done without the user having to do any extra clicks.
I am building a Asp.net C# application i want to post the user uploaded picture directly to Instagram, but after a quick search i can not found any function in the API somebody help me how to post image on instagram wall by C# code
Below Code I can Get Access Token
private void Authentication()
{
string rest = string.Empty;
GlobusInstagramLib.Authentication.ConfigurationIns config = new GlobusInstagramLib.Authentication.ConfigurationIns("https://instagram.com/oauth/authorize/", ConfigurationManager.AppSettings["consumerKey"], ConfigurationManager.AppSettings["consumerSecret"], ConfigurationManager.AppSettings["callbackurl"], "https://api.instagram.com/oauth/access_token", "https://api.instagram.com/v1/", "");
oAuthInstagram _api = oAuthInstagram.GetInstance(config);
rest = _api.AuthGetUrl("likes+comments+basic+relationships");
Response.Redirect(rest);
}
// Call back Url
public ActionResult Instagram()
{
string code = (String)Request.QueryString["code"];
oAuthInstagram objInsta = new oAuthInstagram();
GlobusInstagramLib.Authentication.ConfigurationIns configi = new GlobusInstagramLib.Authentication.ConfigurationIns("https://api.instagram.com/oauth/authorize/", ConfigurationManager.AppSettings["consumerKey"], ConfigurationManager.AppSettings["consumerSecret"], ConfigurationManager.AppSettings["callbackurl"], "http://api.instagram.com/oauth/access_token", "https://api.instagram.com/v1/", "");
oAuthInstagram _api = new oAuthInstagram();
_api = oAuthInstagram.GetInstance(configi);
AccessToken access = new AccessToken();
access = _api.AuthGetAccessToken(code);
string accessToken = access.access_token;
string id =access.user.id;
ViewBag.accessToken = accessToken;`enter code here`
ViewBag.Uid = id;
return View();
}
Below Code i have get Access Token and Profile ID
How To Post Image ? some body tall me
You cannot upload photos to Instagram through the API. From Instagram's API documentation https://instagram.com/developer/endpoints/media/:
At this time, uploading via the API is not possible. We made a
conscious choice not to add this for the following reasons:
Instagram is about your life on the go – we hope to encourage photos from within the app.
We want to fight spam & low quality photos. Once we allow uploading from other sources, it's harder to control what comes into the
Instagram ecosystem. All this being said, we're working on ways to
ensure users have a consistent and high-quality experience on our
platform.
I write a Web Api 2 Project and within one Method (POSTing a new Location), I want to retrieve some Information, the user not provides (Country, City, region).
I found a solution in the Google Maps API:
http://maps.googleapis.com/maps/api/geocode/json?latlng=38.91845,1.44315&sensor=true
This meens, I must only provide the lat and lon coords within this URL.
How can I send this request and process the result within my own API Method?
My method is till now:
public string PostNewLocation(string mName, decimal mLat, decimal mLot)
{
// Here should be the calling and resolving of the Google API
// string mCity = ...
// string mCountry = ...
// Adding new location to database follows here and works fine...
}
public string PostNewLocation(string mName, decimal mLat, decimal mLot)
{
//do a get request to http://maps.googleapis.com/maps/api/geocode/json?latlng=38.91845,1.44315&sensor=true
//deserialize the json response to your own object and do stuff with it
var response = googleAPIService.Get(mLat, mLot);
//do something with the response
}
I am required to integrate a signature pad into an intranet (MVC4) application allowing people to apply electronic signatures to system generated documents. Unfortunately, the signature pad I've been given only has a COM/ActiveX API, so I've written a short Windows Forms application that will allow the user to capture the signature and upload it to the server. When it is uploaded, I need the MVC4 action to associate the signature image with a specified document entity sent by the Windows Forms request. So, say I have this model:
public class DocumentToSign {
public int DocumentId { get; set; }
public int DocumentTypeId { get; set; }
}
Then I have this action to receive the uploaded image:
[HttpPost]
public ActionResult UploadSignature(DocumentToSign doc, HttpPostedFileBase signature)
{
//do stuff and catch errors to report back to winforms app
return Json(new {Success = true, Error = string.Empty});
}
Then, the code to upload the image:
var doc = new DocumentToSign{ DocumentId = _theId, DocumentTypeId = _theType };
var fileName = SaveTheSignature();
var url = GetTheUrl();
using(var request = new WebClient())
{
request.Headers.Add("enctype", "multipart/form-data");
foreach(var prop in doc.GetType().GetProperties())
{
request.QueryString.Add(prop.Name, Convert.ToString(prop.GetValue(doc, null)));
}
var responseBytes = request.UploadFile(url, fileName);
//deserialize resulting Json, etc.
}
The model binder seems to pick up the DocumentToSign class without any problems, but the HttpPostedFileBase is always null. I know that I need to somehow tell the model binder that the uploaded image is the signature parameter in the action, but I can't figure out how to do it. I tried using UploadValues with a NameValueCollection, but NameValueCollection only allows the value to be a string, so the image (even as a byte[]) can't be part of that.
Is it possible to upload a file as well as a model to the same action from outside of the actual MVC4 application? Should I be using something other than HttpPostedFileBase? Other than the WebClient? I am at a loss.
var responseBytes = request.UploadFile(url, fileName); is not sending your file in the format your controller expect.
HttpPostedFileBase works with multipart/form-data POST request. But WebClient.UploadFile is not sending a multipart request, it sends file content as a body of request with no other information.
You can save the file by calling Request.SaveAs(filename, false);
or you have to change the way you are sending the file. But I don't think WebClient support sending multipart requests.