I am new to ASP.NET MVC and learning. So far I have figured out how I can create a JSON Object and return that as a response to a request. However, I'm not able to pass a JSON body as part of a POST request like I normally did using Java.
Here is the code how I did this there -
#Path("/storeMovement")
#POST
#Consumes("application/json")
#Produces("application/json")
public String storeTrace(String json) {
JSONObject response = new JSONObject();
JSONParser parser = new JSONParser();
String ret = "";
try {
Object obj = parser.parse(json);
JSONObject jsonObj = (JSONObject) obj;
RecordMovement re = new RecordMovement((double) jsonObj.get("longitude"), (double) jsonObj.get("latitude"), (long) jsonObj.get("IMSI"));
ret = re.Store();
// Clear object
re = null;
System.gc();
response.put("status", ret);
} catch (Exception e) {
response.put("status", "fail " + e.toString());
}
return response.toJSONString();
}
I tried the same in the ASP.NET Action method but the value in the string parameter a is null as seen while debugging. Here's the code for the Action method -
public string Search(string a)
{
JObject x = new JObject();
x.Add("Name", a);
return x.ToString();
}
It works fine when I use an Object (for example - Book) like so -
public string Search(Book a)
{
JObject x = new JObject();
x.Add("Name", a.Name);
return x.ToString();
}
In that case, the book's name gets de-serialized just fine as I would expect. The class definition for the Book class -
public class Book
{
public int ID { get; set; }
public string Name { get; set; }
}
Can somebody please advise what I'm doing wrong? Is there no way to take in a string and then de-serialize? I'd like to be able to take in JSON without having to use an Object
As for as understand you want pass entire of request body to a string without any binding so you could handle passed string data with your desired way.
To aim this purpose simply write your own model binder:
public class RawBodyBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if(typeof(string)!=bindingContext.ModelType)
return null;
using (var s = new StreamReader(controllerContext.HttpContext.Request.InputStream))
{
s.BaseStream.Position = 0;
return s.ReadToEnd();
}
}
}
And in you action method assign your binder to desired parameter:
public string MyAction([ModelBinder(typeof(RawBodyBinder))]string json)
{
}
But MVC has own JSON model binder as well if your data is a standard JSON and in request body with Content-Type: application/json header you could use MVC's JSON model binder to activate JSON model binder simply add following line in Global.asax.cs file:
protected void Application_Start()
{
// some code here
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}
The first thing in asp.net mvc to post a data is to decorate the method with this attribute [Httpost]
it's mean passing a string
should look like
[HttpPost]
public string Search(string a){
// your code
}
The default value is [HttpGet] that get parameters from url. For Post request you need to.
Edit:
And look the answer from vinayan
with jquery:
$.ajax({
method: "POST",
url: "Home/Search",
data: {'a': 'yourstring'}
})
The name of the parameter you send is used for the de-serialization. So in in this case, "a" should be part of json.
public string Search(string a)
so you will have to use,
$.ajax({
method: "POST",
url: "Home/Search",
data: {'a': 'yourstring'}
})
Related
My controller cannot accept string via POST method. What could be wrong? When I create HttpClient and send content like this :
var content = new FormUrlEncodedContent(new []
{
new KeyValuePair<string, string>("signature", "someexamplecontent"),
});
var response = await _client.PostAsync(path, content);
I'm getting an error: 415, Unsupported media type and it not stepping into controller. Instead, when I use PostAsJsonAsync - stepping into but parameter signature is null.
var response = await _client.PostAsJsonAsync(path, content);
That's a method in a controller:
[HttpPost("generatecert")]
public byte[] PostGenerateCertificate([FromBody] string signature)
{
}
The endpoint is most likely configured for JSON content. If using PostAsJsonAsync then just pass the string to be posted.
var signature = "someexamplecontent";
var response = await _client.PostAsJsonAsync(path, signature);
the method will serialize and set the necessary content type headers for the request.
if posting a more complex object, like
public class Model {
public string signature { get; set; }
public int id { get; set; }
}
The same applies, but the action would need to be updated to expect the complex object
[HttpPost("generatecert")]
public byte[] PostGenerateCertificate([FromBody] Model signature) {
//...
}
and the client would send the object
var model = new Model {
signature = "someexamplecontent",
id = 5
};
var response = await _client.PostAsJsonAsync(path, model);
Reference Parameter Binding in ASP.NET Web API
In .Net MVC, you define routes into a RouteCollection. The URL helper methods make it easy to turn a controller + action + optional params into a URL.
When .Net MVC processes a request from a client browser, it clearly maps this URL to the right controller + action, to execute the appropriate command.
However, I can't see a way to programatically access this routing on the fly, such that I can turn a fully qualified URL (or a list of 10k+ URLs) into it's route components.
Does anyone know how you'd turn, for example, the following string input:
"http://stackoverflow.com/questions/2342325/c-sharp-net-mvc-turning-url-into-controller-action-pair"
into the following output:
{
controller: "questions",
action: "view",
id: 2342325,
seoText: "c-sharp-net-mvc-turning-url-into-controller-action-pair"
}
Given this mapping is clearly being done by .Net, is it exposed anywhere?
Why would anyone want to do this?
Imagine you have a list of URLs you know have been accessed, mostly dynamic in nature, for example stackoverflow.com/questions/2342325/c-sharp-net-mvc-turning-url-into-controller-action-pair, and you want to work out which actual endpoints / actions / controllers are being hit programatically (without much care about the actual data being passed).
You could hand code mappings, such that you know /questions/{id}/{text} -> controller: questions, action: question, but that's not future-proof, nor is it fun, and relies on text manipulation / processing.
Given a Route Dictionary and a list of URLs, with a function as described above, you could look at which controllers are most hit, or which actions, etc.
You should take a look at creating your own MvcRouteHandler. This is the point in the MVC stack where the Route Engine has already parsed the URL to find which Controller and Action to call, and then it goes through this method to get the actual C# class and method to invoke. No authorization or even HTTP Verb has been applied yet, so you will see every call that is made to your application.
public class CustomRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext context)
{
var controller = context.RouteData.Values["controller"];
var action = context.RouteData.Values["action"];
// Do whatever logging you want with this data, maybe grab the other params too.
return base.GetHttpHandler(context);
}
}
This can easily be registered where you set up your Routing.
routes.MapRoute("Home", "{controller}/{action}", new
{
controller = "Home",
action = "Index"
})
.RouteHandler = new CustomRouteHandler();
Looks like the only way to do this is by creating a dummy HTTP Context, similar to how you would unit test routes. It's a shame MVC doesn't provide better access to this, given it's being run on every request, rather than wrapping it up inside the context object.
Anyway, here is a working solution which can be modified to suit your needs:
public class UrlToRouteMapper
{
public static RouteValueDictionary GetRouteDataFromURL(string absoluteURL)
{
var testUrl = "~" + new Uri(absoluteURL).AbsolutePath;
var context = new StubHttpContextForRouting(requestUrl: testUrl);
var routes = new System.Web.Routing.RouteCollection();
MvcApplication.RegisterRoutes(routes);
System.Web.Routing.RouteData routeData = routes.GetRouteData(context);
return routeData.Values;
}
public static string GetEndpointStringFromURL(string absoluteURL)
{
var routeData = GetRouteDataFromURL(absoluteURL);
return routeData["controller"] + "/" + routeData["action"];
}
}
public class StubHttpContextForRouting : HttpContextBase {
StubHttpRequestForRouting _request;
StubHttpResponseForRouting _response;
public StubHttpContextForRouting(string appPath = "/", string requestUrl = "~/") {
_request = new StubHttpRequestForRouting(appPath, requestUrl);
_response = new StubHttpResponseForRouting();
}
public override HttpRequestBase Request {
get { return _request; }
}
public override HttpResponseBase Response {
get { return _response; }
}
}
public class StubHttpRequestForRouting : HttpRequestBase {
string _appPath;
string _requestUrl;
public StubHttpRequestForRouting(string appPath, string requestUrl) {
_appPath = appPath;
_requestUrl = requestUrl;
}
public override string ApplicationPath {
get { return _appPath; }
}
public override string AppRelativeCurrentExecutionFilePath {
get { return _requestUrl; }
}
public override string PathInfo {
get { return ""; }
}
}
public class StubHttpResponseForRouting : HttpResponseBase {
public override string ApplyAppPathModifier(string virtualPath) {
return virtualPath;
}
}
I have created POST/GET request in MVC before.
In my HomeController
[HttpPost]
public string Index(int Value)
{
return Value.ToString();
}
And setting chrome extension POSTMAN with a form-data
I can call http://localhost/mvcApp/ with a variable 'Value' with value '1' and get a string '1' in return
But when I create a surveyController : ApiController doesn't work when I call http://localhost/mvcApp/api/survey/
public string Post(int Value)
{
return Value.ToString();
}
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/mvcApp/api/survey/'.",
"MessageDetail": "No action was found on the controller 'survey' that matches the request."
I'm not sure if the error is in the way the api is created, or in the way the POSTMAN is trying to call the api. Because that '.'
Also try in my HomeControler Index
client.BaseAddress = new Uri("http://localhost/mvcApp");
var result = client.PostAsync("/api/survey", new
{
Value = 1
}, new JsonMediaTypeFormatter()).Result;
if (result.IsSuccessStatusCode) // here return Not found
The WebApi controllers' conventions are not the same as those of plain ol' MVC controllers.
Basically the problem is that you can't specify the int parameter the way you did.
Try this in you WebApi controller:
// nested helper class
public class PostParams {
public int Value { get; set; }
}
public string Post(PostParams parameters) {
return parameters.Value.ToString();
}
and see how that works.
Here's a thorough article on passing parameters within POST requests to WebAPI controllers:
Passing-multiple-POST-parameters-to-Web-API-Controller-Methods
Long story short, these are the conventions, roughly speaking:
you can't capture POST form name-value pairs in parameters
you can capture them inside the properties of a class if that class is the parameter type of one of your method's parameters
you can capture query parameters in method parameters
EDIT
If you wish to test your WebAPI server using C# you could follow these steps:
Create a nice Console Application (preferably within the same solution)
Add the Web API Client NuGet package to this Console Application
Make your Program.cs do something like this.
The following code uses the C# 5.0 async and await operators.
It also uses the Task class and anonymous types.
I've pointed out official MSDN articles (click on the links) should you be interested in what those things are.
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApplication1 {
class Program {
public static void Main(string[] args) {
Test().Wait();
}
private static async Task Test() {
HttpClient client = new HttpClient();
await client.PostAsJsonAsync(
"http://localhost/mvcApp/api/survey/",
new {
value = 10
}
);
}
}
}
This wasnt easy. After lot of reading I solve it like this.
First the api controler need to define the input parameter with the [FromBody] attribute
// POST api/survey
public void Post([FromBody]string value)
{
}
For testing I put a button in the view and use an Ajax / Post, the variable name need to be an empty string before the variable value.
$(document).ready(
$('#post').click(function () {
var url = 'http://localhost/mvcApi/api/survey';
var data = { "": 'Hola' }; // Define a simple variable this way
$.ajax({
type: "POST",
url: url,
data: data,
success: sucess
}
});
})
Or if you want send mutliple values
data = { "": ["update one", "update two", "update three"] };
But if you want receive an object
public void Post(Survey data)
{
string value = data.Value.ToString();
}
$('#post').click(function () {
....
var data = { value: 25 }
More info here Sending Data and here Binding
I am trying to get some data to a controller from client side script, I am stringfying my data so I receive something like:
{"Name":"","Description":"","FieldType":"radio","Fields":[{"Field":{"Name":"something","Value":"nameit"}},{"Field":{"Name":"something else","Value":"dontnameit"}}]}
I will need to validate my data on the controller however, in my action I am recieving a null for some reason, if I use object or string? Why is that?
I have had a look into a lot of other posts but it is not clear, do I need to create my own custom IValueProvider implementation? I think there is one available in the ms futures assembley, I tried to locate the file as I do not want all the code inside the dll, but I could not find it...
Any pointers would be appreciated...
Controller:
[HttpPost]
public JsonResult AddField(string field) //or object
{
//code here
}
Edit: I have followed the post by phill haack but had some errors actually returning the strongly typed object to my view...
my ajax call..
{
url: url,
type: "post",
dataType: 'json',
traditional: true,
data: jsondata, // { "field" : jsondata},
contentType: 'application/json; charset=utf-8',
...
}
I created a custom value provider...
public class Jsonify : ValueProviderFactory
{
public Jsonify() { }
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
var jsonData = GetDeserializedJson(controllerContext);
if (jsonData == null)
{
return null;
}
//currently used by mvc2 futures
//return new DictionaryValueProvider<object>(backingStore,
//CultureInfo.CurrentCulture);
// what do I return?
}
private static object GetDeserializedJson(ControllerContext controllerContext)
{
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
// not JSON request
return null;
}
StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
string bodyText = reader.ReadToEnd();
if (String.IsNullOrEmpty(bodyText))
{
// no JSON data
return null;
}
//json.net
var jsonData = JsonConvert.DeserializeObject<SurveyField>(bodyText);
return jsonData;
}
}
Controller:
public JsonResult AddSimpleField(SurveyField field) { ... }
You may take a look at the following blog post which illustrates how you could use a custom JsonValueProviderFactory to send a JSON encoded string from client scripts to a controller action and have this action receive it as a strongly typed model and benefit from the validation of the default model binder:
[HttpPost]
public ActionResult AddField(SomeViewModel model)
{
if (!ModelState.IsValid)
{
// the client sent an invalid data
return Json(new { IsSuccess = false });
}
// the model passed validation => do some processing with this model
return Json(new { IsSuccess = true });
}
As Phil Haack explains it this custom JsonValueProviderFactory is only necessary if you are working with ASP.NET MVC 2 and is built-in ASP.NET MVC 3 so it should work out of the box.
MVC newbie here:
I've more or less worked out the page navigation aspect of MVC. But let's say I don't want to navigate to a View, but rather I want to get a response out of the web site, e.g. by sending a request to http://mysite.com/Services/GetFoo/123 I want to make a database request to select a Foo object with ID 123 and return it serialized as XML.
How do you do that?
I would write a custom action result:
public class XmlResult : ActionResult
{
private readonly object _data;
public XmlResult(object data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
_data = data;
}
public override void ExecuteResult(ControllerContext context)
{
// You could use any XML serializer that fits your needs
// In this example I use XmlSerializer
var serializer = new XmlSerializer(_data.GetType());
var response = context.HttpContext.Response;
response.ContentType = "text/xml";
serializer.Serialize(response.OutputStream, _data);
}
}
and then in my controller:
public ActionResult GetFoo(int id)
{
FooModel foo = _repository.GetFoo(id);
return new XmlResult(foo);
}
And if this return new XmlResult(foo); feels ugly to your eyes, you could have an extension method:
public static class ControllersExtension
{
public static ActionResult Xml(this ControllerBase controller, object data)
{
return new XmlResult(data);
}
}
and then:
public ActionResult GetFoo(int id)
{
FooModel foo = _repository.GetFoo(id);
return this.Xml(foo);
}
Sounds like you want to create a REST API.
Have a look at Siesta which will do all the heavy lifting.
Alternatively you could write an action method which returns a view which renders as XML rather than HTML.
Something like:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MyModel>" ContentType="text/xml" %>
<%= SerializationHelper.SerializeAsXml(Model) %>
If you could live with a JSON result, the following should work:
public class ServicesController : Controller
{
public ActionResult GetFoo(int id)
{
var dbResult = SomeDbUtil.GetFoo(id);
return this.Json(dbResult);
}
}
This would give you pretty a basic JSON query result. However, if you want your services to be discoverable SOAP XML services etc., setting up another project/website that acts as the web service would seem to be the better idea to me.
You can probably find an answer to your question here:
See Return XML from a controller's action in as an ActionResult?