I'm trying to write a middleware for batch requests i .net core 2.0.
So far the I have splitted the request, pipe each request on to the controllers.
The controllers return value, but for some reason the response on the created context that I parse to the controllers keeps giving me a NullStream in the body, so I think that there is something that I miss in my setup.
The code looks like this:
var json = await streamHelper.StreamToJson(context.Request.Body);
var requests = JsonConvert.DeserializeObject<IEnumerable<RequestModel>>(json);
var responseBody = new List<ResponseModel>();
foreach (var request in requests)
{
var newRequest = new HttpRequestFeature
{
Body = request.Body != null ? new MemoryStream(Encoding.ASCII.GetBytes(request.Body)) : null,
Headers = context.Request.Headers,
Method = request.Method,
Path = request.RelativeUrl,
PathBase = string.Empty,
Protocol = context.Request.Protocol,
Scheme = context.Request.Scheme,
QueryString = context.Request.QueryString.Value
};
var newRespone = new HttpResponseFeature();
var requestLifetimeFeature = new HttpRequestLifetimeFeature();
var features = CreateDefaultFeatures(context.Features);
features.Set<IHttpRequestFeature>(newRequest);
features.Set<IHttpResponseFeature>(newRespone);
features.Set<IHttpRequestLifetimeFeature>(requestLifetimeFeature);
var innerContext = _factory.Create(features);
await _next(innerContext);
var responseJson = await streamHelper.StreamToJson(innerContext.Response.Body);
I'm not sure what it is I'm missing in the setup, since innerContext.Response.Body isn't set.
One of the endpoints that I use for testing and that gets hit looks like this
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
I found the error, or two errors for it to work.
First I had to change my newResponse to
var newRespone = new HttpResponseFeature{ Body = new MemoryStream() };
Since HttpResponseFeature sets Body to Stream.Null in the constructor.
When that was done, then Body kept giving an empty string when trying to read it. That was fixed by setting the Position to Zero like
innerContext.Response.Body.Position = 0;
Related
Maybe I am confused or due to working from home I am not able to think.
I have Web API 2.0 project which is receiving models in end points. I have android client which is sending Date in milliseconds format and for same end point i have MVC client which is sending date in "LOGINDATE": "2020-04-05T01:00:21.45+04:00". the date from MVC is not accepted by API and gives an error on model state like
System.FormatException: DateTime content
'2020-04-05T10:52:42.333+04:00' does not start with '/Date(' and end
with ')/' as required for JSON.
I have forced the WebApi to use the newtonsoft json formatter but when i debug the exception i found
System.Runtime.Serialization.Json.JsonReaderDelegator.ParseJsonDateInDefaultFormat(String originalDateTimeValue)
Following is the WEBAPICONFIG code for forcing use the newtonsoft mediatype.
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.UseDataContractJsonSerializer = true;
JsonSerializerSettings CustomJsonSetting = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
DateTimeZoneHandling = DateTimeZoneHandling.Local,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
//DateFormatString = "yyyy-mm-dd HH:MM:ss"
};
CustomJsonSetting.Converters.Clear();
CustomJsonSetting.Converters.Add(new IsoDateTimeConverter());
JsonConvert.DefaultSettings = () => CustomJsonSetting;
jsonFormatter.SerializerSettings = CustomJsonSetting;
config.Formatters.Clear();
config.Formatters.Insert(0,jsonFormatter);
Then from MVC client i am compressing GZIP and sending the call like following:
var frmt = UniversalFormatters.GetJsonFormatter();
if (IsCompressionEnabled)
{
var json = JsonConvert.SerializeObject(request, frmt.SerializerSettings);
//new JavaScriptSerializer().Serialize(request);
//
client.DefaultRequestHeaders.Add("pHubCompression", "1");
var content = new CompressedContent(
new StringContent(json, Encoding.UTF8, "application/json"),
CompressionMethod.GZip);
return await client.PostAsync(EndPoint, content);
}
else
return await client.PostAsync(EndPoint, request, frmt);
And Universal Formatter is defined as
public static JsonMediaTypeFormatter GetJsonFormatter()
{
if (JsonFormatter==null)
{
JsonFormatter = new JsonMediaTypeFormatter();
JsonFormatter.UseDataContractJsonSerializer = true;
JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
JsonFormatter.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
JsonFormatter.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
JsonFormatter.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
JsonFormatter.SerializerSettings.DateFormatString = "yyyy-MM-ddTHH:mm:ss";
}
return JsonFormatter;
}
I have done a bit R&D and people mentioned to create my own date formater which is a work around but i wanted to know if there is something wrong with my setting.
My Model is defined like following:
[System.Runtime.Serialization.DataContractAttribute(Namespace = "http://MyProj.Core.Entities", Name = "RequestObject{0}")]
public class RequestObject<T> : IDisposable//, IEntity
//where T : IEntity, IEntityCollection, ICompositeEntity
{
private UserInfo m_userInfo;
private T m_obj;}
and UserInfo is defined like
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Namespace = "http://MyProj.Business.Contracts", Name = "UserInfo")]
public partial class UserInfo : BaseEntity, IEntity, IDisposable
{
[System.Runtime.Serialization.DataMember(EmitDefaultValue = true)]
public DateTime? LOGINDATE { set; get; }}
Sorry for my bad English.
according to above comment by #dbc I removed JsonFormatter.UseDataContractJsonSerializer = true; this line from my startups and everything starts working. Thanks for the help.
When made I post request is made its giving internal server. Is the implementation of Flurl is fine or I am doing something wrong.
try
{
Models.PaymentPost paymentPost = new Models.PaymentPost();
paymentPost.Parts = new Models.Parts();
paymentPost.Parts.Specification = new Models.Specification();
paymentPost.Parts.Specification.CharacteristicsValue = new List<Models.CharacteristicsValue>();
paymentPost.Parts.Specification.CharacteristicsValue.Add(new Models.CharacteristicsValue { CharacteristicName = "Amount", Value = amount });
paymentPost.Parts.Specification.CharacteristicsValue.Add(new Models.CharacteristicsValue { CharacteristicName = "AccountReference", Value = accountId });
foreach (var item in extraParameters)
{
paymentPost.Parts.Specification.CharacteristicsValue.Add(new Models.CharacteristicsValue {
CharacteristicName = item.Key, Value = item.Value });
}
var paymentInJson = JsonConvert.SerializeObject(paymentPost);
var selfCareUrl = "http://svdt5kubmas01.safari/auth/processPaymentAPI/v1/processPayment";
var fUrl = new Flurl.Url(selfCareUrl);
fUrl.WithBasicAuth("***", "********");
fUrl.WithHeader("X-Source-System", "POS");
fUrl.WithHeader("X-Route-ID", "STKPush");
fUrl.WithHeader("Content-Type", "application/json");
fUrl.WithHeader("X-Correlation-ConversationID", "87646eaa-2605-405e-967c-56e8002b5");
fUrl.WithHeader("X-Route-Timestamp", "150935");
fUrl.WithHeader("X-Source-Operator", " ");
var response = await clientFactory.Get(fUrl).Request().PostJsonAsync(paymentInJson).ReceiveJson<IEnumerable<IF.Models.PaymentPost>>();
return response;
}
catch (FlurlHttpException ex)
{
dynamic d = ex.GetResponseJsonAsync();
//string s = ex.GetResponseStringAsync();
return d;
}
You don't need to do this:
var paymentInJson = JsonConvert.SerializeObject(paymentPost);
PostJsonAsync just takes a regular object and serializes it to JSON for you. Here you're effectively double-serializing it and the server is probably confused by that format.
You're also doing a lot of other things that Flurl can do for you, such as creating those Url and client objects explicitly. Although that's not causing errors, this is how Flurl is typically used:
var response = await selfCareUrl
.WithBasicAuth(...)
.WithHeader(...)
...
.PostJsonAsync(paymentPost)
.ReceiveJson<List<IF.Models.PaymentPost>>();
I have Controller whit some endpoints Task<IActionResult> MyCustomEndpoint which is returning return Ok(CustomDataType). Returned datas are in JSON fromat.
Now I want to call that Endpoint from other Controller like var resp = myController.MyCustomEndpoint, where resp becomes IActionResult. The problem is that resp now doesn't return only datas anymore, but all of those fields as seen on image.
My question is, how to access and return only Value field, because resp.Value is not working.
Thanks for help.
You have to convert to as ObjectResult
public async Task GetFormSummary_ItShouldReturnSingleForm_NotNull()
{
var formId = new Guid("5EF685E7-1167-4226-7F5E-08D9009544A3");
var mockFormService = new Mock<IFormService>();
mockFormService.Setup(x => x.GetAsync(formId).Result)
.Returns(GetTestForm());
// Inject
var formController = new FormController(_logger, mockFormService.Object);
// Act
var result = (await formController.GetAsync(formId)) as ObjectResult;
// Assert
Assert.IsAssignableFrom<FormViewModel>(result.Value);
}
I've never had to do this before, because it's always only been an actual form that I've posted as that content type, but recently I had to post three variables like that, and I resorted to a sordid concatenation with & and =:
var content = new StringContent("grant_type=password&username=" + username + "&password=" + password.ToClearString(), Encoding.UTF8,
"application/x-www-form-urlencoded");
I'm sure there must be a utility method that would do that, and do it better, with any necessary encoding. What would that be?
If this is a POCO and just using the Newtonsoft library, you can use this as well:
public static class FormUrlEncodedContentExtension
{
public static FormUrlEncodedContent ToFormUrlEncodedContent(this object obj)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
var keyValues = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
var content = new FormUrlEncodedContent(keyValues);
return content;
}
}
And a sample usage would be:
var myObject = new MyObject {Grant_Type = "TypeA", Username = "Hello", Password = "World"};
var request = new HttpRequestMessage(HttpMethod.Post, "/path/to/post/to")
{
Content = myObject.ToFormUrlEncodedContent()
};
var client = new HttpClient {BaseAddress = new Uri("http://www.mywebsite.com")};
var response = await client.SendAsync(request);
Use reflection to get the property names and values and then use them to create a System.Net.Http.FormUrlEncodedContent
public static class FormUrlEncodedContentExtension {
public static FormUrlEncodedContent ToFormUrlEncodedContent(this object obj) {
var nameValueCollection = obj.GetType()
.GetProperties()
.ToDictionary(p => p.Name, p => (p.GetValue(obj) ?? "").ToString());
var content = new FormUrlEncodedContent(nameValueCollection);
return content;
}
}
From there it is a simple matter of calling the extension method on an object to convert it to a FormUrlEncodedContent
var model = new MyModel {
grant_type = "...",
username = "...",
password = "..."
};
var content = model.ToFormUrlEncodedContent();
You should be able to use string interpolation for that. Something like:
var content = new StringContent($"grant_type=password&username={username}&password={password}", Encoding.UTF8, "application/x-www-form-urlencoded");
Or wrap this inside a helper/factory method:
public static class StringContentFactory
{
public static StringContent Build(string username, string password)
{
return new StringContent($"grant_type=password&username={username}&password={password}", Encoding.UTF8, "application/x-www-form-urlencoded");
}
}
I'm going crazy!
I'm using Azure Machine Learning and R Script. I deploy it as Web Service. I use sample code based on HttpClient.
using (var client = new HttpClient())
{
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"input1",
new StringTable()
{
ColumnNames = new string[] {
"experts_estimates",
"experts_share_of_unique_information",
"avg_correlation",
"point_a",
"point_b",
"is_export_mode"
},
Values = new string[,] {
{
expertsEstimatesStr,
expertsShareOfUniqueInformationStr,
avgCorrelationStr,
pointAStr,
pointBStr,
isExportModeStr
},
}
}
},
},
GlobalParameters = new Dictionary<string, string>()
{
}
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.BaseAddress = new Uri(apiUrl);
// WARNING: The 'await' statement below can result in a deadlock
// if you are calling this code from the UI thread of an ASP.Net application.
// One way to address this would be to call ConfigureAwait(false)
// so that the execution does not attempt to resume on the original context.
// For instance, replace code such as:
// result = await DoSomeTask()
// with the following:
// result = await DoSomeTask().ConfigureAwait(false)
HttpResponseMessage response = await client.PostAsJsonAsync("", scoreRequest);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return result;
}
else
{
// Print the headers - they include the requert ID and the timestamp,
// which are useful for debugging the failure
var headers = response.Headers.ToString();
string responseContent = await response.Content.ReadAsStringAsync();
throw new Exception(responseContent, new Exception(headers));
}
}
and when I run code from Visual Studio I get:
but when I run code from Azure App Service I get:
Any ideas?
One solution is adding "edit metadata" module inside the model and rename the output columns. It'll be easy than using the code to name the columns.