Why does my web API post method get null parameter? - asp.net

I have a web API project with a controller like this:
namespace Api.Controllers
{
public class StudyController : ApiController
{
[Route("api/PostReviewedStudyData")]
[HttpPost]
public bool PostReviewedStudyData([FromBody]string jsonStudy)
{
ApiStudy study = JsonHelper.JsonDeserialize<ApiStudy>(jsonStudy);
BusinessLogics.BL.SaveReviewedStudyDataToDb(study);
return true;
}
[Route("api/GetStudyData/{studyUid}")]
[HttpGet, HttpPost]
public string GetStudyData(string studyUid)
{
ApiStudy study = BusinessLogics.BL.GetStudyObject(studyUid);
return JsonHelper.JsonSerializer<ApiStudy>(study);
}
}
}
I call it like this, from another application:
HttpWebRequest httpWReq = (HttpWebRequest)WebRequest.Create(#"http://localhost:60604/api/PostReviewedStudyData");
ASCIIEncoding encoding = new ASCIIEncoding();
string postData = Api.JsonHelper.JsonSerializer<ApiStudy>(s);
byte[] data = encoding.GetBytes(postData);
httpWReq.Method = "POST";
httpWReq.ContentType = "application/json; charset=utf-8";
httpWReq.ContentLength = data.Length;
httpWReq.Accept = "application/json";
using (Stream stream = httpWReq.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
HttpWebResponse response = (HttpWebResponse)httpWReq.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
My breakpoint at the post method is hit, but the jsonStudy object is null. Any Ideas?

First of all what i notice is this:
HttpWebRequest httpWReq = (HttpWebRequest)WebRequest.Create(#"http://localhost:60604/api/PostReviewedStudy Data");
you have a space in the PostReviewedStudy Data also if that does not work try removing the content type line and see if it works

Try the following:
[Route("api/PostReviewedStudyData")]
[HttpPost]
public bool PostReviewedStudyData([FromBody]ApiStudy study)
{
BusinessLogics.BL.SaveReviewedStudyDataToDb(study);
return true;
}
WebApi supports fully typed parameters, there's no need to convert from a JSON string.

Related

Error during serialization or deserialization using JSON JavascriptSerializer

I have error
"Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property." when I try to retrieve my data.
What have I try is to put below code in web.config and it is still not working.
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="50000000"/>
</webServices>
</scripting>
</system.web.extensions>
</configuration>
Can anyone help to advise on this?
Changing the default maxJsonLength in web config won't work when trying to return a JsonResult, because asp.net will try to serialize your object internally while completely ignoring the maxJsonLenght you set.
One simple way to fix this, is to return a string instead of a JsonResult in your method, and instance a new JavaScriptSerializer with your custom maxJsonLenght, something like this:
[HttpPost]
public string MyAjaxMethod()
{
var veryBigJson = new object();
JavaScriptSerializer s = new JavaScriptSerializer
{
MaxJsonLength = int.MaxValue
};
return s.Serialize(veryBigJson);
}
And then in your view you just parse it back into a json with JSON.parse(data)
Another way would be creating your own JsonResult class when you can actually control the maxJsonLenght, something like this:
public class LargeJsonResult : JsonResult
{
const string JsonRequest_GetNotAllowed = "This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.";
public LargeJsonResult(object data,JsonRequestBehavior jsonRequestBehavior=JsonRequestBehavior.DenyGet,int maxJsonLength=Int32.MaxValue)
{
Data = data;
JsonRequestBehavior = jsonRequestBehavior;
MaxJsonLength = maxJsonLength;
RecursionLimit = 100;
}
public new int MaxJsonLength { get; set; }
public new int RecursionLimit { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(JsonRequest_GetNotAllowed);
}
HttpResponseBase response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/json";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer() { MaxJsonLength = MaxJsonLength, RecursionLimit = RecursionLimit };
response.Write(serializer.Serialize(Data));
}
}
}
And on your method you just change the type and return a new instance of the class
[HttpPost]
public LargeJsonResult MyAjaxMethod()
{
var veryBigJson = new object();
return new LargeJsonResult(veryBigJson);
}
Source: https://brianreiter.org/2011/01/03/custom-jsonresult-class-for-asp-net-mvc-to-avoid-maxjsonlength-exceeded-exception/
Can you do it like this in your code
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
myObject obj = serializer.Deserialize<yourObject>(yourJsonString);

Read Asp.Net Core Response body in ActionFilterAttribute

I'm using Asp.Net Core as a Rest Api Service.
I need access to request and response in ActionFilter. Actually, I found the request in OnActionExcecuted but I can't read the response result.
I'm trying to return value as follow:
[HttpGet]
[ProducesResponseType(typeof(ResponseType), (int)HttpStatusCode.OK)]
[Route("[action]")]
public async Task<IActionResult> Get(CancellationToken cancellationToken)
{
var model = await _responseServices.Get(cancellationToken);
return Ok(model);
}
And in ActionFilter OnExcecuted method as follow:
_request = context.HttpContext.Request.ReadAsString().Result;
_response = context.HttpContext.Response.ReadAsString().Result; //?
I'm trying to get the response in ReadAsString as an Extension method as follow:
public static async Task<string> ReadAsString(this HttpResponse response)
{
var initialBody = response.Body;
var buffer = new byte[Convert.ToInt32(response.ContentLength)];
await response.Body.ReadAsync(buffer, 0, buffer.Length);
var body = Encoding.UTF8.GetString(buffer);
response.Body = initialBody;
return body;
}
But, there is no result!
How I can get the response in OnActionExcecuted?
Thanks, everyone for taking the time to try and help explain
If you're logging for json result/ view result , you don't need to read the whole response stream. Simply serialize the context.Result:
public class MyFilterAttribute : ActionFilterAttribute
{
private ILogger<MyFilterAttribute> logger;
public MyFilterAttribute(ILogger<MyFilterAttribute> logger){
this.logger = logger;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
var result = context.Result;
if (result is JsonResult json)
{
var x = json.Value;
var status = json.StatusCode;
this.logger.LogInformation(JsonConvert.SerializeObject(x));
}
if(result is ViewResult view){
// I think it's better to log ViewData instead of the finally rendered template string
var status = view.StatusCode;
var x = view.ViewData;
var name = view.ViewName;
this.logger.LogInformation(JsonConvert.SerializeObject(x));
}
else{
this.logger.LogInformation("...");
}
}
I know there is already an answer but I want to also add that the problem is the MVC pipeline has not populated the Response.Body when running an ActionFilter so you cannot access it. The Response.Body is populated by the MVC middleware.
If you want to read Response.Body then you need to create your own custom middleware to intercept the call when the Response object has been populated. There are numerous websites that can show you how to do this. One example is here.
As discussed in the other answer, if you want to do it in an ActionFilter you can use the context.Result to access the information.
For logging whole request and response in the ASP.NET Core filter pipeline you can use Result filter attribute
public class LogRequestResponseAttribute : TypeFilterAttribute
{
public LogRequestResponseAttribute() : base(typeof(LogRequestResponseImplementation)) { }
private class LogRequestResponseImplementation : IAsyncResultFilter
{
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var requestHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Request.Headers);
Log.Information("requestHeaders: " + requestHeadersText);
var requestBodyText = await CommonLoggingTools.FormatRequestBody(context.HttpContext.Request);
Log.Information("requestBody: " + requestBodyText);
await next();
var responseHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Response.Headers);
Log.Information("responseHeaders: " + responseHeadersText);
var responseBodyText = await CommonLoggingTools.FormatResponseBody(context.HttpContext.Response);
Log.Information("responseBody: " + responseBodyText);
}
}
}
In Startup.cs add
app.UseMiddleware<ResponseRewindMiddleware>();
services.AddScoped<LogRequestResponseAttribute>();
Somewhere add static class
public static class CommonLoggingTools
{
public static async Task<string> FormatRequestBody(HttpRequest request)
{
//This line allows us to set the reader for the request back at the beginning of its stream.
request.EnableRewind();
//We now need to read the request stream. First, we create a new byte[] with the same length as the request stream...
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
//...Then we copy the entire request stream into the new buffer.
await request.Body.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
//We convert the byte[] into a string using UTF8 encoding...
var bodyAsText = Encoding.UTF8.GetString(buffer);
//..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
request.Body.Position = 0;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
public static async Task<string> FormatResponseBody(HttpResponse response)
{
//We need to read the response stream from the beginning...
response.Body.Seek(0, SeekOrigin.Begin);
//...and copy it into a string
string text = await new StreamReader(response.Body).ReadToEndAsync();
//We need to reset the reader for the response so that the client can read it.
response.Body.Seek(0, SeekOrigin.Begin);
response.Body.Position = 0;
//Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
return $"{response.StatusCode}: {text}";
}
public static string SerializeHeaders(IHeaderDictionary headers)
{
var dict = new Dictionary<string, string>();
foreach (var item in headers.ToList())
{
//if (item.Value != null)
//{
var header = string.Empty;
foreach (var value in item.Value)
{
header += value + " ";
}
// Trim the trailing space and add item to the dictionary
header = header.TrimEnd(" ".ToCharArray());
dict.Add(item.Key, header);
//}
}
return JsonConvert.SerializeObject(dict, Formatting.Indented);
}
}
public class ResponseRewindMiddleware {
private readonly RequestDelegate next;
public ResponseRewindMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
Stream originalBody = context.Response.Body;
try {
using (var memStream = new MemoryStream()) {
context.Response.Body = memStream;
await next(context);
//memStream.Position = 0;
//string responseBody = new StreamReader(memStream).ReadToEnd();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
} finally {
context.Response.Body = originalBody;
}
}
You can also do...
string response = "Hello";
if (result is ObjectResult objectResult)
{
var status = objectResult.StatusCode;
var value = objectResult.Value;
var stringResult = objectResult.ToString();
responce = (JsonConvert.SerializeObject(value));
}
I used this in a .net core app.
Hope it helps.

EndGetResponse can only be called once for each asynchronous operation?

Trying to implement a WebRequest and return to the caller synchronously.
I have tried various implementations and I think this would be the most appropriate so far.
Unfortunately the following code throws an InvalidOperationException with the message
EndGetResponse can only be called once for each asynchronous operation
I really struggled enough to make this happen and its really vital to the library I build to use the WebRequest like this.
The following code is intend to use in Windows Phone 8 and Windows 8 platforms.
I already understand the async/await pattern and used it, but it is REALLY vital for me to use the synchronous version of the web service request in a part of my library.
The code:
public void ExecuteRequest(string url, string requestData)
{
WebRequest request = WebRequest.Create(new Uri(url));
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.Headers["Header-Key"] = "AKey";
DTOWebRequest webRequestState = new DTOWebRequest
{
Data = requestData,
Request = request
};
ManualResetEventSlim resetEventSlim = new ManualResetEventSlim(false);
// Begin the request using a delegate
request.BeginGetRequestStream(ar =>
{
DTOWebRequest requestDataObj = (DTOWebRequest )ar.AsyncState;
HttpWebRequest requestStream = (HttpWebRequest)requestDataObj.Request;
string data = requestDataObj.Data;
// Convert the string into a byte array.
byte[] postBytes = Encoding.UTF8.GetBytes(data);
try
{
// End the operation
using (Stream endGetRequestStream = requestStream.EndGetRequestStream(ar))
{
// Write to the request stream.
endGetRequestStream.Write(postBytes, 0, postBytes.Length);
}
// Get the response using a delegate
requestStream.BeginGetResponse(result =>
{
DTOWebRequest requestDataObjResult = (DTOWebRequest )ar.AsyncState;
HttpWebRequest requestResult = (HttpWebRequest)requestDataObjResult.Request;
try
{
// End the operation
using (HttpWebResponse response = (HttpWebResponse)requestResult.EndGetResponse(ar)) // Here the exception is thrown.
{
HttpStatusCode rcode = response.StatusCode;
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
// The Response
string responseString = streamRead.ReadToEnd();
if (!string.IsNullOrWhiteSpace(requestDataObjResult.FileName))
{
FileRepository fileRepo = new FileRepository();
fileRepo.Delete(requestDataObjResult.FileName);
}
Debug.WriteLine("Response : {0}", responseString);
}
}
catch (WebException webEx)
{
WebExceptionStatus status = webEx.Status;
WebResponse responseEx = webEx.Response;
Debug.WriteLine(webEx.ToString());
}
resetEventSlim.Set(); // Signal to return handler
}, requestDataObj);
}
catch (WebException webEx)
{
WebExceptionStatus status = webEx.Status;
WebResponse responseEx = webEx.Response;
Debug.WriteLine(webEx.ToString());
}
}, webRequestState);
resetEventSlim.Wait(5000); // Wait either for Set() or a timeout 5 secs.
}
}
Thank you.
You can't do synchronous web calls in Windows Phone and that's why you aren't.
If you were, you'd be calling GetRequestStream instead of BeginGetRequestStram/EndGetRequestStream.
The only reason to be synchronous on Windows Phone is to block the UI which is a very bad idea.
You should use an HttpClient and àsync-await` instead.
But if you really think you should (and can) do asynchronous calls on Windows Phone, you can always try something like this:
public void ExecuteRequest(string url, string requestData)
{
try
{
WebRequest request = WebRequest.Create(new Uri(url));
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.Headers["Header-Key"] = "AKey";
// Convert the string into a byte array.
byte[] postBytes = Encoding.UTF8.GetBytes(requestData);
using (var requestStream = request.EndGetRequestStream(request.BeginGetRequestStream(null, null)))
{
// Write to the request stream.
endGetRequestStream.Write(postBytes, 0, postBytes.Length);
}
using (var response = request.EndGetResponse(request.BeginGetResponse(null, null)))
{
using (var streamRead = new StreamReader(response.GetResponseStream()))
{
// The Response
string responseString = streamRead.ReadToEnd();
if (!string.IsNullOrWhiteSpace(requestDataObjResult.FileName))
{
var fileRepo = new FileRepository();
fileRepo.Delete(request.FileName);
}
Debug.WriteLine("Response : {0}", responseString);
}
}
}
catch (WebException webEx)
{
WebExceptionStatus status = webEx.Status;
WebResponse responseEx = webEx.Response;
Debug.WriteLine(webEx.ToString());
}
}
But I really think you should revise your decision/need.

Consume form data by wcf service sending by post

I read some articles about this and I find that to achive that wcf get data from post request we add
[ServiceContract]
public interface IService1 {
[OperationContract]
[WebInvoke(
Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/GetData")]
void GetData(Stream data);
}
and in implementation
public string GetData( Stream input)
{
long incomingLength = WebOperationContext.Current.IncomingRequest.ContentLength;
string[] result = new string[incomingLength];
int cnter = 0;
int arrayVal = -1;
do
{
if (arrayVal != -1) result[cnter++] = Convert.ToChar(arrayVal).ToString();
arrayVal = input.ReadByte();
} while (arrayVal != -1);
return incomingLength.ToString();
}
My question is what should I do that in submit action in form request will send to my service and consume?
In Stream parameter will I have post information from form to which I could get by Request["FirstName"]?
Your code isn't decoding the request body correctly - you're creating an array of string values, each one with one character. After getting the request body, you need to parse the query string (using HttpUtility is an easy way to do so). The code below shows how to get the body and one of the fields correctly.
public class StackOverflow_7228102
{
[ServiceContract]
public interface ITest
{
[OperationContract]
[WebInvoke(
Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/GetData")]
string GetData(Stream data);
}
public class Service : ITest
{
public string GetData(Stream input)
{
string body = new StreamReader(input).ReadToEnd();
NameValueCollection nvc = HttpUtility.ParseQueryString(body);
return nvc["FirstName"];
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
c.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
Console.WriteLine(c.UploadString(baseAddress + "/GetData", "FirstName=John&LastName=Doe&Age=33"));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

WebInvoke with UriTemplate with empty strings

How does the WebInvokeAttribute and UriTemplate resolver behave when supplied with empty strings in placeholders at runtime?
The documentation doesn't seem to cover this.
In some inherited code, I'm getting situations where the methods are not being resolved properly when empty strings are passed. There is no obvious conflict with other web methods.
Thanks!
Update:
In a UriTemplate such as: "/{x}/{y}?z={z}", what is the behavior if some or all of the values are provided as "" empty strings, but the delimiters remain, "/17/?z=", "//apple?z=", "//?z=%20", "//?z=". Also, by standard, are browsers allowed to clean up URIs before sending them?
The empty string means that the URI for the operation is located at the same address as the endpoint - see the example below for more information.
public class StackOverflow_6267866
{
[ServiceContract]
public interface ITest1
{
[WebInvoke(UriTemplate = "")]
string EchoString(string text);
}
[ServiceContract]
public interface ITest2
{
[WebInvoke(UriTemplate = "", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
int Add(int x, int y);
}
public class Service : ITest1, ITest2
{
public string EchoString(string text)
{
return text;
}
public int Add(int x, int y)
{
return x + y;
}
}
static void SendPostRequest(string uri, string contentType, string body)
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
req.Method = "POST";
req.ContentType = contentType;
byte[] bodyBytes = Encoding.UTF8.GetBytes(body);
req.GetRequestStream().Write(bodyBytes, 0, bodyBytes.Length);
req.GetRequestStream().Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string headerName in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", headerName, resp.Headers[headerName]);
}
Console.WriteLine();
Stream respStream = resp.GetResponseStream();
Console.WriteLine(new StreamReader(respStream).ReadToEnd());
Console.WriteLine();
Console.WriteLine(" *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ");
Console.WriteLine();
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest1), new WebHttpBinding(), "ITest1").Behaviors.Add(new WebHttpBehavior());
host.AddServiceEndpoint(typeof(ITest2), new WebHttpBinding(), "ITest2").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
SendPostRequest(baseAddress + "/ITest1", "application/json", "\"hello world\"");
SendPostRequest(baseAddress + "/ITest1/", "application/json", "\"hello world\"");
SendPostRequest(baseAddress + "/ITest2", "application/json", "{\"x\":123,\"y\":456}");
SendPostRequest(baseAddress + "/ITest2/", "application/json", "{\"x\":123,\"y\":456}");
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Resources