Reading the parameters sent in the body from applet to the post method of web api controller - asp.net

Consider the following class
public class StController:apicontroller {
public void PostBodyMethod() {
HttpRequestMessage request=this.request;
//How to read the header and body parameters
}
}
The applet sends both the header and body parameters to the post method.
How to retrieve the information which is sent along with post method inside the webapi controller using the HttpRequestMessage object?

If the body parameter is a JSON object, all what you need is to just pass Model parameter in Post method. Web API supports json by default. You might need to read this.
To reader headers in HttpRequest, you can use:
var headers = ControllerContext.Request.Headers;
Sample code:
class Model
{
public int Id { get; set; }
public int Hj { get; set; }
}
public class StController : ApiController {
public void Post(Model model) {
//How to read the header and body parameters
var headers = ControllerContext.Request.Headers;
}
}

Related

Swagger UI doesn't render body parameter field for my complex type parameter in GET action of my Controller

I have an ASP.NET Web API 2 project to which I have added Swagger - Swashbuckle v5.6.0. Everything works fine. Swagger UI renders test endpoints for my API as expected.
I added a new Controller to my API. There is a GET action with a complex type parameter. For complex types, Web API tries to read the value from the message body. This is the default behaviour.
Here is my GET action:
[HttpGet]
[Route("search")]
[ResponseType(typeof(List<SearchModel>))]
public IHttpActionResult Search(SearchModel searchOptions)
{
//....
return Ok();
}
And her is my complex type:
public class SearchModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType(DataType.EmailAddress)]
[EmailAddress]
public string Email { get; set; }
public string AddressLine1 { get; set; }
public string City { get; set; }
public string Telephone { get; set; }
public string MobilePhone { get; set; }
}
The problem:
But Swagger UI doesn't render body parameter field for my complex type in the GET action. For POST and PUT actions Swagger UI renders body parameter fields as expected but not for the complex type in my GET action.
As can be seen in the screenshot Swagger UI renders query parameters fields for attributes in my complex type instead of rendering a body parameter field for my type as it does in the case of POST and PUT.
My GET action is working fine when testing from Postman and filling the json in the body of the request. By setting breakpoint in the action inside Visual Studio I can see the values are bound to my object in the action parameter.
I have tried to decorate the parameter in my action with [FromBody] (which is the default for complex type) but same result.
Is this a bug in Swagger? Or am I missing something?
Sadly, you can't do what you want with Swagger. You can't send a request model in an HTTP GET method. You can however change the swagger UI to look like this:
but you won't be able to receive the model in your controller.
This is a known issue within the Swagger developers and it was discussed in 2016 and the final decision is that swagger won't support a request body in an HTTP GET method. Here is the link to the already closed issue.
You have three options here:
Leave the method as it is, and test it in Postman, but not in Swagger.
Follow the below steps to achieve the picture above, but please note, that it will only fix the UI part and you will always end up with null SearchModel in the controller when you press Try it out! in swagger.
Make it a [HttpPost method instead of [HttpGet].
How to make swagger UI display GET method with request body:
First, create one Attribute class:
public class ModelInBodyAttribute : Attribute
{
public ModelInBodyAttribute(string modelName, string description, bool isRequired)
{
this.ModelName = modelName;
this.Description = description;
this.IsRequired = IsRequired;
}
public string ModelName { get; set; }
public bool IsRequired { get; set; }
public string Description { get; set; }
}
Then you can decorate your method in the controller:
[ModelInBody(modelName: nameof(SearchModel), description: "My model description", isRequired: true)]
[HttpGet]
[Route("search")]
[ResponseType(typeof(List<SearchModel>))]
public IHttpActionResult Search(SearchModel searchOptions)
{
//....
return Ok(new List<SearchModel>());
}
After that create IOperationFilter class (ModelInBodyOperationFilter):
public class ModelInBodyOperationFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var attribute = apiDescription.GetControllerAndActionAttributes<ModelInBodyAttribute>().FirstOrDefault();
if (attribute == null)
{
return;
}
operation.parameters.Clear();
operation.parameters.Add(new Parameter
{
name = attribute.ModelName,
description = attribute.Description,
#in = "body",
required = attribute.IsRequired,
schema = new Schema { #ref = $"#/definitions/{attribute.ModelName}" }
});
}
}
Lastly, don't forget to register the IOperationFilter in SwaggerConfig:
c.OperationFilter<ModelInBodyOperationFilter>();
When you send the request via swagger, you will notice that the Curl part is absolutely correct, but still, in your controller there is nothing.
There are endless discussions on whether you should have a PAYLOAD "Body content" in a GET request. As you mentioned it's supported by HTTP but you will find in the internet that many people suggest not to do it. I guess that swagger team also expect you not to use it.

How to use axios.put to send JSON to ASP.NET controller

I'm trying to send a PUT request with JSON data using the following client code:
const url = new URL(`${process.env.REACT_APP_API}/datas/edit/${id}`);
axios.put(url, data);
And on the server side, when I'm trying to look at in the HttpRequest.Form, the Controller throws InvalidOperationException exception . The message is Incorrect Content-Type: application/json;charset=UTF-8.
[HttpPut("edit/{id}")]
public void Edit([FromRoute] int id, [FromBody] Data data)
...
I also tried axios.put(url, JSON.stringify(data)); but server returns 415.
EDIT: I tried with Postman instead of my front-end:
public class A { public int /*or string*/ A1 { get; set; } }
[HttpPut("edit/{id}")]
public void EditQuestion([FromRoute] int id, [FromBody] A a) ...
IMPORTANT:
I shouldn't have looked at HttpRequest.Form because I'm sending JSON data and it should be parsed into my model.
I see the json response from postman returns a string, instead your "A" class has an int property for A1.
Try changing the class to:
public class A { public string A1 { get; set; } }

Passing dynamic variable to custom action filter from controller's HTTPGET method

I have an MVC app where controller A calls an internal HTTPGET method (handled by controller B). A has a view and B doesn't.
The HTTPGET in the controller B looks like this :
[HttpGet]
public String GetToken(string accessToken, string UID) {
....
// Log errors and other metrics
return someToken;
}
I want to use an action filter with my B controller which does the error logging for me. I do need the parameters passed with HTTP GET while logging. How can I pass accessToken and UID to the action filter such that I can log it.
What I'm looking for is something like this :
The controller should be something like
[MyActionFilter]
[HttpGet]
public String GetToken(string accessToken, string UID) {
....
return someToken;
}
while the action filter should do the logging
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void onActionExecuted(HttpActionExecutedContext actionExecutedContext) {
// READ THE HTTP GET PARAMETERS AND DO THE LOGGING
}
}
You can use this:
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void onActionExecuted(
ActionExecutedContext actionExecutedContext) {
// READ THE HTTP GET PARAMETERS AND DO THE LOGGING
actionExecutedContext.HttpContext.Request.QueryString; // HTTP GET
actionExecutedContext.HttpContext.Request.Params;
// HTTP GET / POST / COOKIE PARAMETERS As Key Value List
}
}
Best way would be to Log QueryString and other Items as suggested by other answers,
however if you want to access only the Method Parameters then you can do it
as shown below, ActionParameters Dictionary will give you all method parameters.
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void OnActionExecuted
(HttpActionExecutedContext filterContext) {
foreach (KeyValuePair<string,object> item
in filterContext.ActionParameters)
{
//print item.Key
//print item.Value
}
}
}
I solved this by making the required parameters public in the controller, and reading the params directly as filterContext.Controller.publicParam.
The ActionFilter now looks like this -
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var thisController = ((myController)filterContext.Controller);
// public function in the controller that returns the required object
RequiredParameters requiredParams =
thisController.getParametersForLogging( );
// read the variables from requiredParams object and do the logging
}

ASP.NET MVC API model not parsing

I am having some problems parsing my model in ASP.NET MVC API
This is my API controller:
public class UserController : ApiController
{
// Hent liste af personer
public IEnumerable<UserModel> Get()
{
return new UserModel[] { new UserModel(), new UserModel() };
}
// Hente enkelt person
public UserModel Get(int id)
{
return new UserModel();
}
// Opret person
[ValidationActionFilter]
public CreateUserRespose Post([FromBody]UserModel model)
{
CreateUserRespose rs = new CreateUserRespose();
return rs;
}
// Rediger person
public UserModel Put(int id, [FromBody]UserModel model)
{
return new UserModel();
}
// Slet person
public UserModel Delete(int id)
{
return new UserModel();
}
}
}
And the UserModel:
public class UserModel
{
[Required]
[StringLength(500)]
public String FristName { get; set; }
[Required]
[StringLength(500)]
public String LastName { get; set; }
[Required]
[StringLength(250)]
public String Email { get; set; }
[Required]
public String MatrikelId { get; set; }
}
When I call though Fiddler to the Post command with the following body
FirstName=Fistname MiddleName&LastName=SomeName&Email=email#email.us&MatrikelId=1234
Will the action Post be called, but the model is null, and ModelState.IsValid is true, the same happens if I send no data with the body!
What am I doing wrong here?
Update:
I have tryed sending the data as json instead
Fiddler:
User-Agent: Fiddler
Host: localhost:51268
Content-Length: 102
Content-type: application/json
{"FristName":"Kasper asdasd","LastName":"asdasdasd","Email":"asdaasd#asdasd.us","MatrikelId":"132456asd"}
But should the model state not be invalid when the model is null?
The ASP.NET Web API is using content negotiation process in order to decide which MediaTypeFormatter to use for deserializing the body of the request. For the typical POST request it will check for Accept and Content-Type headers. If none is present it will use the first MediaTypeFormatter on the list (by default it is JsonMediaTypeFormatter).
In your case Web API was unable to determine the proper MediaTypeFormatter. Adding a Content-Type header with value of application/x-www-form-urlencoded to the request should resolve the issue.
If you want to get more detailed knowledge regarding Formatters, Model Binding and Content Negotiation in ASP.NET Web API I would suggest following reading:
Designing Evolvable Web APIs with ASP.NET -> Chapter 8. Formatters and Model Binding (you should look very close at the entire book if you are interesed in learning ASP.NET Web API)
Everything you want to know about ASP.NET Web API content negotiation

Pass a JSON array to a WCF web service

I am trying to pass a JSON array to a WCF service. But it doesn't seem to work. I actually pulled an array [GetStudents] out the service and sent the exact same array back to the service [SaveStudents] and nothing (empty array) was received.
The JSON array is of the format:
[
{"Name":"John","Age":12},
{"Name":"Jane","Age":11},
{"Name":"Bill","Age":12}
]
And the contracts are of the following format:
//Contracts
[DataContract]
public class Student{
[DataMember]public string Name { get; set; }
[DataMember]public int Age{ get; set; }
}
[CollectionDataContract(Namespace = "")]
public class Students : List<Student>
{
[DataMember]public Endorsements() { }
[DataMember]public Endorsements(IEnumerable<Student> source) : base(source) { }
}
//Operations
public Students GetStudents()
{
var result = new Students();
result.Add(new Student(){Name="John",12});
result.Add(new Student(){Name="Jane",11});
result.Add(new Student(){Name="Bill",12});
return result;
}
//Operations
public void SaveStudents(Students list)
{
Console.WriteLine(list.Count); //It always returns zero
}
It there a particular way to send an array to a WCF REST service?
I had similar issue.
I was calling the service from a browser and the problem was Firefox dynamically changing the request content-type from 'application/json' to 'application-json;charset=utf-8'.
If you are calling the service from a browser, test it with non-firefox browser and if that was the case you need to remove the charset from the request content-type header

Resources