POSTing to my ASP.NET MVC app from 3rd party site - asp.net-mvc-3

I'm testing a payment provider (SagePay) and as part of a process, their server POSTs to my site and expects a response. I can't get this to work using MVC.
I set up a classic asp test reponse page and added it to my MVC app:
<%
Response.Buffer = True
response.Clear()
response.contenttype="text/plain"
response.write "Status=OK" & vbCRLF
response.write "RedirectURL=http://www.redirectsomewhere.co.uk" & vbCRLF
response.End()
%>
This work fine.
However, when I try to do the same with MVC, it doesn't work:
Controller:
[HttpPost]
public ActionResult TestCallback()
{
return View();
}
View:
#{
Response.Buffer = true;
Response.Clear();
Response.ContentType = "text/plain";
Response.Write("Status=OK" + System.Environment.NewLine);
Response.Write("RedirectURL=http://www.redirectsomewhere.co.uk" + System.Environment.NewLine);
Response.End();
}
The error message is a generic error from the payment provider so is no real help, but I have narrowed the error down to the point at which the page renders.
I can browse to both pages fine (i need remove the HttpPost attribute from the MVC controller method for this), and both pages display identical data.
This is the MVC url that the payment provider is POSTing to:
http://myipaddress/CA_UAT/Token/TestCallback
This is the classic asp URL that works fine:
http://myipaddress/CA_UAT/Token/TestCallback.asp
I created a 'Token' directory for the asp page so the urls would match for testing purposes.
What am I doing wrong?
UPDATE
In response to Hari's comment, I installed a Firefox plugin called 'Header Spy' which gives me this information:
Response HTTP/1.1 200 OK
Source: Response
HttpHeader:Server
Request:User-Agent Cookie
Response:Response Date Set-Cookie
Both pages show the same info.

You don't need to return an action result in order to send just plain text back to the screen. The simplest way of accomplishing this is to return a string value. Replace the code in your controller with what is below.
[HttpPost]
public string TestCallback()
{
string result = "Status=OK";
result += System.Environment.NewLine;
result += "RedirectURL=http://www.redirectsomewhere.co.uk";
result += System.Environment.NewLine;
return result;
}
This will return no other response that what you have in the string. By using an ActionResult and View you are likely returning markup from the master view.

Instead of writing the response in the view, I would write it in the action method like this:
[HttpPost]
public ActionResult TestCallback()
{
Response.Buffer = true;
Response.Clear();
Response.ContentType = "text/plain";
Response.Write("Status=OK" + System.Environment.NewLine);
Response.Write("RedirectURL=http://www.redirectsomewhere.co.uk" + System.Environment.NewLine);
Response.Flush();
return new EmptyResult();
}
When returning EmptyResult you will ensure that MVC doesn't append anything to the response.

Try like this:
[HttpPost]
public ActionResult TestCallback()
{
var sb = new StringBuilder();
sb.AppendLine("Status=OK");
sb.AppendLine("RedirectURL=http://www.redirectsomewhere.co.uk");
return Content(sb.ToString(), "text/plain");
}
or in a more MVCish way:
View model:
public class ResponseViewModel
{
public string Status { get; set; }
public string RedirectUrl { get; set; }
}
and then a custom action result:
public class StatusActionResult : ContentResult
{
private readonly ResponseModel _model;
public StatusActionResult(ResponseModel model)
{
_model = model;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "text/plain";
response.Write(string.Format("Status={0}{1}", _model.Status, Environment.NewLine));
response.Write(string.Format("RedirectURL={0}", _model.RedirectUrl));
}
}
and finally your controller action:
[HttpPost]
public ActionResult TestCallback()
{
var model = new ResponseModel
{
Status = "OK",
RedirectUrl = "http://www.redirectsomewhere.co.uk"
};
return new StatusActionResult(model);
}

I wonder if sagepay is expecting a file extension..ie doing some kind of URL validation on heir side. Do you know if your Action is being invoked?
Also try adding a route that makes your mvc URL look like "TestCallback.asp".

Related

ASP.NET HTTP POST and Redirect

I have users that will be directed to my application from an external site, and during this redirecting, user's email addresses will be sent to me by HTTP POST.
What I'm trying to accomplish is to receive user's email address, and send the user to the Index view.
I have the following methods:
[HttpGet]
public ActionResult Index()
{
string email = "";
string[] keys = Request.Form.AllKeys;
for (int i = 0; i < keys.Length; i++)
{
Response.Write(keys[i] + ": " + Request.Form[keys[i]] + "<br>");
System.Diagnostics.Debug.WriteLine(keys[i] + ": " + Request.Form[keys[i]]);
email = Request.Form[keys[i]].ToString();
}
return Index(email);
}
[HttpPost]
public ActionResult Index(string email)
{
return View();
}
And this is the method I have to mimic the external site's job, which was redirecting user to my view with user's email address.
public ActionResult Httppost()
{
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["email"] = "test#email.com";
var response = wb.UploadValues("http://localhost:57695/Home/Index", "POST", data);
}
return RedirectToAction("Index");
}
The problem is, Index never receives the email address from my Httppost() method, and email is always received as "". How can I receive the email from Httppost() into Index()?
Why are you sending the email address in a separate request? These two requests have nothing to do with one another. The data received in one isn't available to the other, and they're not guaranteed to happen in the same order.
Instead of this:
var response = wb.UploadValues("http://localhost:57695/Home/Index", "POST", data);
return RedirectToAction("Index");
Just send the value in one request so that it will be available in that request:
return RedirectToAction("Index", new { email = "test#email.com" });
Then you only need one action:
[HttpGet]
public ActionResult Index(string email)
{
// use the email parameter
}
Side note: I don't think this is going to do what you expect:
return Index(email);
That's going to look for a view named after the value of the email variable. Because of how method overloading works, you can't really use a string by itself as a model. You're going to want to either set it on an actual model or put it in something like ViewBag instead.

How do you read POST data in an ASP.Net MVC 3 Web API 2.1 controller?

This does not seem to be as easy as I thought. I found some solutions on the web, but they are not working for me. I have an ASP.Net MVC 3 project with the Microsoft ASP.Net Web API 2.1 nuget package installed. Now, I want to be able to read data posted to a web api controller. The data sent will vary, so I cannot used a strongly typed ViewModel.
Here are the solutions I tried:
public void Post([FromBody]string value)
{
...
}
public void Post([FromBody]List<string> values)
{
...
}
public void Post([FromBody]NameValueCollection values)
{
...
}
But my value or values variables are always empty. I know the controller is receiving data however because I can check it by accessing (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"].Request.Form. It does not look like the proper way to retrieve the data though. There ought to be a cleaner way.
UPDATE:
Here is how I am posting the information:
I am posting the data from another controller in the same web application:
public ActionResult SendEmailUsingService()
{
dynamic email = new ExpandoObject();
email.ViewName = "EmailTest";
email.From = "fromaddress#yahoo.com";
email.To = "toaddress#gmail.com";
email.Fullname = "John Smith";
email.Url = "www.mysite.com";
IDictionary<string, object> data = email;
using (var wb = new WebClient())
{
string url = BaseUrlNoTrailingSlash + Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Emailer" });
var response = wb.UploadValues(url, "POST", data.ToNameValueCollection());
}
return View();
}
And here is what I am getting in my Post web api controller if I declare an httpContext variable like this:
var httpContext = (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"];
httpContext.Request.Form =
{ViewName=EmailTest&From=fromaddress%40yahoo.com&To=toaddress%40gmail.com&Fullname=John+Smith&Url=www.mysite.com}
httpContext.Request.Form is a System.Collections.Specialized.NameValueCollection {System.Web.HttpValueCollection}
I finally found the answer to my question here:
Web API Form Data Collection
The solution is to use FormDataCollection:
public void Post([FromBody]FormDataCollection formData)
{
...
}

Post Scalar data type using HttpClient.PostAsJsonAsync

I am invoking ASP .Net Web API using HttpClient and invoke actions successfully. Also I am able to POST custom object into action as well.
Now problem I am facing is, not able to post scalar data type like Integer,String etc...
Below is my controller and application code that invokes action
// Test application that invoke
[Test]
public void RemoveCategory()
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
HttpResponseMessage response = client.PostAsJsonAsync<string>("http://localhost:49931/api/Supplier/RemoveCategory/", "9").Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
// Controller and Action in Web API
public class SupplierController : ApiController
{
NorthwindEntities context = new NorthwindEntities();
[HttpPost]
public HttpResponseMessage RemoveCategory(string CategoryID)
{
try
{
int CatId= Convert.ToInt32(CategoryID);
var category = context.Categories.Where(c => c.CategoryID == CatId).FirstOrDefault();
if (category != null)
{
context.Categories.DeleteObject(category);
context.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK, "Delete successfully CategoryID = " + CategoryID);
}
else
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, "Invalid CategoryID");
}
}
catch (Exception _Exception)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, _Exception.Message);
}
}
When I Post custome object that represent "Category" table in Northwind database all things working properly but I am not able to post scalar data like Integer and String
When I am post string data type I am getting following exception
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:49931/api/Supplier/RemoveCategory/'.","MessageDetail":"No action was found on the controller 'Supplier' that matches the request."}
Can anyone guide me?
You will have to mark your CategoryID parameter as [FromBody]:
[HttpPost]
public HttpResponseMessage RemoveCategory([FromBody] string CategoryID)
{ ... }
By default, simple types such as string will be model bound from the URI.

asp.net mvc3: How to return raw html to view?

Are there other ways I can return raw html from controller? As opposed to just using viewbag. like below:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.HtmlOutput = "<HTML></HTML>";
return View();
}
}
#{
ViewBag.Title = "Index";
}
#Html.Raw(ViewBag.HtmlOutput)
There's no much point in doing that, because View should be generating html, not the controller. But anyways, you could use Controller.Content method, which gives you ability to specify result html, also content-type and encoding
public ActionResult Index()
{
return Content("<html></html>");
}
Or you could use the trick built in asp.net-mvc framework - make the action return string directly. It will deliver string contents into users's browser.
public string Index()
{
return "<html></html>";
}
In fact, for any action result other than ActionResult, framework tries to serialize it into string and write to response.
Simply create a property in your view model of type MvcHtmlString. You won't need to Html.Raw it then either.
Give a try to return bootstrap alert message, this worked for me
return Content("<div class='alert alert-success'><a class='close' data-dismiss='alert'>
×</a><strong style='width:12px'>Thanks!</strong> updated successfully</div>");
Note: Don't forget to add bootstrap css and js in your view page
hope helps someone.
What was working for me (ASP.NET Core), was to set return type ContentResult, then wrap the HMTL into it and set the ContentType to "text/html; charset=UTF-8". That is important, because, otherwise it will not be interpreted as HTML and the HTML language would be displayed as text.
Here's the example, part of a Controller class:
/// <summary>
/// Startup message displayed in browser.
/// </summary>
/// <returns>HTML result</returns>
[HttpGet]
public ContentResult Get()
{
var result = Content("<html><title>DEMO</title><head><h2>Demo started successfully."
+ "<br/>Use <b>Swagger</b>"
+ " to view API.</h2></head><body/></html>");
result.ContentType = "text/html; charset=UTF-8";
return result;
}
That looks fine, unless you want to pass it as Model string
public class HomeController : Controller
{
public ActionResult Index()
{
string model = "<HTML></HTML>";
return View(model);
}
}
#model string
#{
ViewBag.Title = "Index";
}
#Html.Raw(Model)
public ActionResult Questionnaire()
{
return Redirect("~/MedicalHistory.html");
}
In controller you can use MvcHtmlString
public class HomeController : Controller
{
public ActionResult Index()
{
string rawHtml = "<HTML></HTML>";
ViewBag.EncodedHtml = MvcHtmlString.Create(rawHtml);
return View();
}
}
In your View you can simply use that dynamic property which you set in your Controller like below
<div>
#ViewBag.EncodedHtml
</div>

Return an other action result as string

In my MVC website, I am creating a small forum. For a single post I am rendering my "Single(Post post)" action in my "PostController" like below
<% Html.RenderAction<PostController>(p => p.Single(comment)); %>
Also When a user reply a post I am sending reply as an ajax request to my "CreatePost" action then return "Single" view as result of this action like below
public ActionResult CreatePostForForum(Post post)
{
//Saving post to DB
return View("Single", postViewData);
}
When I do like that only the view is being rendered, Codes in "Single" Actions body isn't beig executed.
What is the best way to do this?
Also I want to return "Single" action result as string in my JsonObject like below
return Json(new{IsSuccess = true; Content= /*HERE I NEED Single actions result*/});
You can use something like this, but be very careful with this. It can actually cause badly traceable errors (for example when you forget to explicitly set view name in Single method).
public ActionResult Single(PostModel model) {
// it is important to explicitly define which view we should use
return View("Single", model);
}
public ActionResult Create(PostModel model) {
// .. save to database ..
return Single(model);
}
Cleaner solution would be to do the same as if it was post from standard form - redirect (XMLHttpRequest will follow it)
For returning ajax views wrapped in json I use following class
public class AjaxViewResult : ViewResult
{
public AjaxViewResult()
{
}
public override void ExecuteResult(ControllerContext context)
{
if (!context.HttpContext.Request.IsAjaxRequest())
{
base.ExecuteResult(context);
return;
}
var response = context.HttpContext.Response;
response.ContentType = "application/json";
using (var writer = new StringWriter())
{
var oldWriter = response.Output;
response.Output = writer;
try
{
base.ExecuteResult(context);
}
finally
{
response.Output = oldWriter;
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
response.Write(serializer.Serialize(new
{
action = "replace",
html = writer.ToString()
}));
}
}
}
It is probably not the best solution, but it works quite well. Note that you will need to manually set View, ViewData.Model, ViewData, MasterName and TempData properties.
My recommendation:
Post your forum reply (and whatever options) via Ajax.
Return your JSONResult, using this method: ASP MVC View Content as JSON to render your content.
In the OnSuccess handler of your ajax call, check if IsSuccess is true. If successful, append the content to the appropriate container using JQuery

Resources