How could I use this action as a service?
public class HomeController : Controller {
public string GetSomeValue(){
return "This is some value";
}
}
If I navigate to this URL, http://mysite.com/Home/GetSomeValue, it returns a string, without any html or markup of any kind.
So, what is to keep me from using this method as a service that returned something meaningful, say json, that I could call from anywhere?
And if this is possible, how would I do this (Say from the code behind of another asp.net web site)?
Thanks in advance.
100% Nothing wrong with doing this.
A sample application - NerdDinner - does this very same thing to load dinners restfully.
See http://nerddinner.codeplex.com/SourceControl/changeset/view/70027#874260 for controller and http://nerddinner.codeplex.com/SourceControl/changeset/view/70027#874293 for javascript file ( look for NerdDinner.FindMostPopularDinners )
e.g.
C#
// AJAX: /Search/GetMostPopularDinners
// AJAX: /Search/GetMostPopularDinners?limit=5
[HttpPost]
public ActionResult GetMostPopularDinners(int? limit)
{
var dinners = dinnerRepository.FindUpcomingDinners();
// Default the limit to 40, if not supplied.
if (!limit.HasValue)
limit = 40;
var mostPopularDinners = from dinner in dinners
orderby dinner.RSVPs.Count descending
select dinner;
var jsonDinners =
mostPopularDinners.Take(limit.Value).AsEnumerable()
.Select(item => JsonDinnerFromDinner(item));
return Json(jsonDinners.ToList());
}
JS
NerdDinner.FindMostPopularDinners = function (limit) {
$.post("/Search/GetMostPopularDinners", { "limit": limit }, NerdDinner._renderDinners, "json");
}
This is essentially a RESTful service:
http://www.ibm.com/developerworks/webservices/library/ws-restful/
All you gonna need is to construct the http request to consume this service, you could use Hammock to construct such requests:
https://github.com/danielcrenna/hammock
Related
I'm working on an angular 2 app using a ASP.Net MVC backend. For this, the index.cshtml has included #Html.AntiForgeryToken(); so i get back a hidden input element with the Token as its value. In my frontend i can grab this token and use it for my requests. So far so good.
Now since user information is used to generate the token in the backend, i have to switch out the token on some occassions. The problem is described here in this question.
Now since i don't want to do a full page reload in my app i have to use a workaround. For this i created a partial view in the backend which just hands out this input using #Html.AntiForgeryToken(); as ActionResult with a simple method like this:
public ActionResult GetAntiForgery()
{
return PartialView("~/Views/Home/AntiForgery.cshtml");
}
After login / logout i call this function from my frontend and replace the value of my existing AntiForgery input element like this:
getAntiForgery(url:string) {
return this._http.get(url)
.map((response:any) => {
let originalXsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
let body = response._body;
if(body) {
let helperElem = document.createElement('div');
helperElem.innerHTML = body;
let helperInputElem = <HTMLInputElement>helperElem.firstChild;
originalXsrfElement.value = helperInputElem.value;
}
return response;
});
}
I can't even tell you what bugs me the most. I hate to make an extra request (but lets not dive into this here) but way more terrible for me is that i have to request something, get an html string back and have to extract the token string out of it.
If i were the backend guy, i would kill the frontend guy (me..) for even thinking about creating an extra partial view, creating an extra method and always doing two requests on login/logout instead of one.
Is there a better way? For example i would like to call a method which just hands out a JSON with the proper token instead of an HTML snippet. Even better, on existing JsonResult methods in the backend, i would like to add the new CSRF Token as a property.
I'm not a backend architect so there might be some stuff wrong in general how i do this, but my backend colleagues don't mind what i'm doing there so it shouldn't be so far off.
Any hints are appreciated.
You can use AntiForgery.GetToken in your action to set some property on the JSON being returned:
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
json.RequestVerificationToken = cookieToken + ":" + formToken;
Then, pass it back in the header of your next AJAX request:
$.ajax("/my/awesome/url", {
type: "post",
contentType: "application/json",
data: { ... },
dataType: "json",
headers: {
'RequestVerificationToken': requestVerificationToken
}
});
Since, it's no longer being handled by cookies, you can't just use the standard ValidateAntiForgeryToken attribute. You'll need to manually check for the header and validate it. However, you should be able to create a custom attribute you can use instead:
AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateAntiForgeryTokenFromHeaderAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
}
Then, just decorate your actions with [ValidateAntiForgeryTokenFromHeader] instead.
[Code adapted from http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks]
Using ASP.Net Web API service I can get the current windows user using the following.
public class UserController : ApiController
{
public string Get()
{
var id = WindowsIdentity.GetCurrent();
return id.Name;
}
}
My question is how can I find the current user logged in a angularjs controller without having to call the web api service?
myApp.controller('TestCtrl', function ($scope) {
$scope.getUserData = function(){
$http({method: 'GET', url: '/URLtoResourceInWebService'}).
success(function(data, status, headers, config) {
//use the data of your User object
}).error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
}); //End of Controller
Here is a real simple example of how to hit an endpoint in Angular and get back a resource from a WebService. I would actually suggest extracting out your API calls however into a service rather than using the "$http" because then you centralize them in one place, and if you switch API's your code doesn't break all over. Let me know if this helps.
From my MVC application, I am trying to make a POST request to these sample end-points (actions) in an API controller named MembershipController:
[HttpPost]
public string GetFoo([FromBody]string foo)
{
return string.Concat("This is foo: ", foo);
}
[HttpPost]
public string GetBar([FromBody]int bar)
{
return string.Concat("This is bar: ", bar.ToString());
}
[HttpPost]
public IUser CreateNew([FromBody]NewUserAccountInfo newUserAccountInfo)
{
return new User();
}
Here's the client code:
var num = new WebAPIClient().PostAsXmlAsync<int, string>("api/membership/GetBar", 4).Result;
And here's the code for my WebAPIClient class:
public class WebAPIClient
{
private string _baseUri = null;
public WebAPIClient()
{
// TO DO: Make this configurable
_baseUri = "http://localhost:54488/";
}
public async Task<R> PostAsXmlAsync<T, R>(string uri, T value)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var requestUri = new Uri(client.BaseAddress, uri);
var response = await client.PostAsXmlAsync<T>(requestUri, value);
response.EnsureSuccessStatusCode();
var taskOfR = await response.Content.ReadAsAsync<R>();
return taskOfR;
}
}
}
I have the following default route defined for the Web API:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
UPDATE
My code breaks into the debugger until the time the PostAsXmlAsync method on the System.Net.HttpClient code is called. However, no request shows up in Fiddler.
However, if I try to compose a POST request in Fiddler or try to fire a GET request via the browser to one of the API end-points, the POST request composed via Fiddler tells me that I am not sending any data and that I must. The browser sent GET request rightly tells me that the action does not support a GET request.
It just seems like the System.Net.HttpClient class is not sending the POST request properly.
One of the most usual problems is that you don't use the appropriate attribute.
Take into account that there are attributes for ASP.NET MVC and ASP.NET Web API with the same name, but which live in different namespaces:
For Web API you must use the one in System.Web.Http
For MVC, the one in System.Web.MVc
This is a very very usual error, and it affects to allkind of things that exist for both MVC and Web API. So you must be very careful when using something which can exists in bith worlds (for example filters, attributes, or dependency injection registration).
I experienced a similar problem (may not be same one though). In my case, I hadn't given name attribute to the input element. I only figured that out when fiddler showed no post data being sent to the server (just like your case)
<input id="test" name="xyz" type="text" />
Adding the name attribute in the input tag fixed my problem.
However, there is one more thing to note. WebAPI does not put form data into parameters directly. Either you have to create an object with those properties and put that object in the parameter of the post controller. Or you could put no parameters at all like this:
[Route("name/add")]
public async Task Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
return;
}
var provider = PostHelper.GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
var clientId = result.FormData["xyz"];
...
Try changing the FromBody to FromUri.
If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Remove FromBody at all and don't make any restrictions in passing parameters (it can be passed at this time either in uri, query string or form submissions (which is kinda a similar to query strings)
[HttpPost]
public string GetFoo(string foo){...}
It will be implicitly parsed and passed.
I am developing a Restful service using .net web API.
There are a few posts about input validation for the post request using model validation. However, I am wondering what is the best practice of doing the validation for Get request.
For example
public HttpResponseMessage Get(int id, string type)
{
// validation
if (id <= 500 & id >= 0) {
// invalid request
throw new HttpResponseException();
}
// type validation
if (type is not in a predefined allowed type list from database) {
// throw validation error
}
// more validation ... ...
// do something else
}
I would like to know what is the best place to put the validation logic in in .net web api framework.
The id validation is just an example and the validation logic could go quite complicated for some cases.
I don't want to create a class just for the id and put some custom validator attribute on the ID property. I think .net has a better support for that already.
You can use route constraints for this parameter
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { id = "#[1-500]+" } //this is not valid code. use correct regular expression to implement validation behavior you need.
);
Answer on comment. What u mean - complicated vlidation? You asked about GET request and the siplest way is use the route constraint. Another way is ActiontFilter. For example
public class SomeFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
string param = filterContext.ActionArguments["id"].ToString();
//do some validation stuff here. for example data anotations
var validator = new RangeAttribute(1, 500); //numeric range.
if (validator.IsValid(Convert.ToInt64(param)));
do valid//
//if u need validate entire model from post request try
if (!filterContext.ModelState.IsValid)
{
filterContext.Response = filterContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, filterContext.ModelState);
}
}
}
or google for "web api model validation"
I have a jQuery $.post back to a MVC 4 Controller that will return back a PartialViewResult with rendered using the data sent in the POST. When debugging the Partial View and Controller, the correct data is being received and sent to the Partial View as the View Model. The issue is, when analyzing the HTML sent back in the AJAX result it is containing seemingly "cached" data from the original page refresh.
I have seen a good amount of posts on here that are similar, but none that were the same as my issue.
I am aware that HTTP Post requests do not cache in the browser, so that is not the issue. I also have set the set the OutputCache attribute to NoStore = true, etc.
Controller
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public partial class MyController : Controller
{
...
[HttpPost]
public virtual ActionResult UpdatePartial(MyViewModel myVm)
{
return this.PartialView("My/_Partial", myVm);
}
}
JS
$('.someButton').click(function () {
$.post(myAjaxUrl, $('form').serialize(), function (data) {
$('#myContent').html(data);
});
});
I'm able to work around this by adding ModelState.Clear prior to doing any operations on the model.
[HttpPost]
public virtual ActionResult UpdatePartial(PersonViewModel model)
{
ModelState.Clear();
model.FirstName += "1";
model.LastName += "1";
model.Age += 1;
return this.PartialView("../My/_Partial", model);
}
This question has an answer by Tim Scott with more info an links.
By default JQuery will cache $.ajax XMLHttpRequests (unless the data type is script or jsonp). Since $.post is implemented via $.ajax, JQuery itself has cached your request. The following JS should work.
JS
$('.someButton').click(function () {
$.ajax{(
url: myAjaxUrl,
data: myAjaxUrl,
success: function (data) {
$('#myContent').html(data);
},
cache: false
});
});
You might also find it worthwhile to handle the error event in case the post doesn't succeed.