I am new to Web API and REST services and looking to build a simple REST server which accepts file uploads. I found out grapevine which is simple and easy to understand. I couldn't find any file upload example?
This is an example using System.Web.Http
var streamProvider = new MultipartFormDataStreamProvider(ServerUploadFolder);
await Request.Content.ReadAsMultipartAsync(streamProvider);
but the grapevine Request property does not have any method to do that. Can someone point me to an example?
If you are trying to upload a file as a binary payload, see this question/answer on GitHub.
If you are trying to upload a file from a form submission, that will be a little bit trickier, as the multi-part payload parsers haven't been added yet, but it is still possible.
The following code sample is complete untested, and I just wrote this off the top of my head, so it might not be the best solution, but it's a starting point:
public static class RequestExtensions
{
public static IDictionary<string, string> ParseFormUrlEncoded(this IHttpRequest request)
{
var data = new Dictionary<string, string>();
foreach (var tuple in request.Payload.Split('='))
{
var parts = tuple.Split('&');
var key = Uri.UnescapeDataString(parts[0]);
var val = Uri.UnescapeDataString(parts[1]);
if (!data.ContainsKey(key)) data.Add(key, val);
}
return data;
}
public static IDictionary<string, FormElement> ParseFormData(this IHttpRequest request)
{
var data = new Dictionary<string, FormElement>();
var boundary = GetBoundary(request.Headers.Get("Content-Type"));
if (boundary == null) return data;
foreach (var part in request.Payload.Split(new[] { boundary }, StringSplitOptions.RemoveEmptyEntries))
{
var element = new FormElement(part);
if (!data.ContainsKey(element.Name)) data.Add(element.Name, element);
}
return data;
}
private static string GetBoundary(string contenttype)
{
if (string.IsNullOrWhiteSpace(contenttype)) return null;
return (from part in contenttype.Split(';', ',')
select part.TrimStart().TrimEnd().Split('=')
into parts
where parts[0].Equals("boundary", StringComparison.CurrentCultureIgnoreCase)
select parts[1]).FirstOrDefault();
}
}
public class FormElement
{
public string Name => _dispositionParams["name"];
public string FileName => _dispositionParams["filename"];
public Dictionary<string, string> Headers { get; private set; }
public string Value { get; }
private Dictionary<string, string> _dispositionParams;
public FormElement(string data)
{
var parts = data.Split(new [] { "\r\n\r\n", "\n\n" }, StringSplitOptions.None);
Value = parts[1];
ParseHeaders(parts[0]);
ParseParams(Headers["Content-Disposition"]);
}
private void ParseHeaders(string data)
{
Headers = data.TrimStart().TrimEnd().Split(new[] {"\r\n", "\n"}, StringSplitOptions.RemoveEmptyEntries).Select(header => header.Split(new[] {':'})).ToDictionary(parts => parts[0].TrimStart().TrimEnd(), parts => parts[1].TrimStart().TrimEnd());
}
private void ParseParams(string data)
{
_dispositionParams = new Dictionary<string, string>();
foreach (var part in data.Split(new[] {';'}))
{
if (part.IndexOf("=") == -1) continue;
var parts = part.Split(new[] {'='});
_dispositionParams.Add(parts[0].TrimStart(' '), parts[1].TrimEnd('"').TrimStart('"'));
}
}
}
If you are looking for something async to use immediately, you can try to implement the answer to this stackoverflow question, which has not been tested by me.
Related
I need to be able to mock IDocumentQuery, to be able to test piece of code, that queries document collection and might use predicate to filter them:
IQueryable<T> documentQuery = client
.CreateDocumentQuery<T>(collectionUri, options);
if (predicate != null)
{
documentQuery = documentQuery.Where(predicate);
}
var list = documentQuery.AsDocumentQuery();
var documents = new List<T>();
while (list.HasMoreResults)
{
documents.AddRange(await list.ExecuteNextAsync<T>());
}
I've used answer from https://stackoverflow.com/a/49911733/212121 to write following method:
public static IDocumentClient Create<T>(params T[] collectionDocuments)
{
var query = Substitute.For<IFakeDocumentQuery<T>>();
var provider = Substitute.For<IQueryProvider>();
provider
.CreateQuery<T>(Arg.Any<Expression>())
.Returns(x => query);
query.Provider.Returns(provider);
query.ElementType.Returns(collectionDocuments.AsQueryable().ElementType);
query.Expression.Returns(collectionDocuments.AsQueryable().Expression);
query.GetEnumerator().Returns(collectionDocuments.AsQueryable().GetEnumerator());
query.ExecuteNextAsync<T>().Returns(x => new FeedResponse<T>(collectionDocuments));
query.HasMoreResults.Returns(true, false);
var client = Substitute.For<IDocumentClient>();
client
.CreateDocumentQuery<T>(Arg.Any<Uri>(), Arg.Any<FeedOptions>())
.Returns(query);
return client;
}
Which works fine as long as there's no filtering using IQueryable.Where.
My question:
Is there any way to capture predicate, that was used to create documentQuery and apply that predicate on collectionDocuments parameter?
Access the expression from the query provider so that it will be passed on to the backing collection to apply the desired filter.
Review the following
public static IDocumentClient Create<T>(params T[] collectionDocuments) {
var query = Substitute.For<IFakeDocumentQuery<T>>();
var queryable = collectionDocuments.AsQueryable();
var provider = Substitute.For<IQueryProvider>();
provider.CreateQuery<T>(Arg.Any<Expression>())
.Returns(x => {
var expression = x.Arg<Expression>();
if (expression != null) {
queryable = queryable.Provider.CreateQuery<T>(expression);
}
return query;
});
query.Provider.Returns(_ => provider);
query.ElementType.Returns(_ => queryable.ElementType);
query.Expression.Returns(_ => queryable.Expression);
query.GetEnumerator().Returns(_ => queryable.GetEnumerator());
query.ExecuteNextAsync<T>().Returns(x => new FeedResponse<T>(query));
query.HasMoreResults.Returns(true, true, false);
var client = Substitute.For<IDocumentClient>();
client
.CreateDocumentQuery<T>(Arg.Any<Uri>(), Arg.Any<FeedOptions>())
.Returns(query);
return client;
}
The important part is where the expression passed to the query is used to create another query on the backing data source (the array).
Using the following example subject under test for demonstration purposes.
public class SubjectUnderTest {
private readonly IDocumentClient client;
public SubjectUnderTest(IDocumentClient client) {
this.client = client;
}
public async Task<List<T>> Query<T>(Expression<Func<T, bool>> predicate = null) {
FeedOptions options = null; //for dummy purposes only
Uri collectionUri = null; //for dummy purposes only
IQueryable<T> documentQuery = client.CreateDocumentQuery<T>(collectionUri, options);
if (predicate != null) {
documentQuery = documentQuery.Where(predicate);
}
var list = documentQuery.AsDocumentQuery();
var documents = new List<T>();
while (list.HasMoreResults) {
documents.AddRange(await list.ExecuteNextAsync<T>());
}
return documents;
}
}
The following sample tests when an expression is passed to the query
[TestMethod]
public async Task Should_Filter_DocumentQuery() {
//Arrange
var dataSource = Enumerable.Range(0, 3)
.Select(_ => new Document() { Key = _ }).ToArray();
var client = Create(dataSource);
var subject = new SubjectUnderTest(client);
Expression<Func<Document, bool>> predicate = _ => _.Key == 1;
var expected = dataSource.Where(predicate.Compile());
//Act
var actual = await subject.Query<Document>(predicate);
//Assert
actual.Should().BeEquivalentTo(expected);
}
public class Document {
public int Key { get; set; }
}
I have a WebService that is working and receiving files using the POST method, but in which I also need to receive data, simultaneously.
ASP.NET WebApi code:
public Task<HttpResponseMessage> Post()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
string file1 = provider.FileData.First().LocalFileName;
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
And the client, developed for Android, is sending the file and the data like this (the send of the file is tested and working, the sending of the data is still not tested, as I need it to be working in the server side):
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Sample Text Content"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileName + ".png\""),
RequestBody.create(MEDIA_TYPE_PNG, bitmapdata))
.addFormDataPart("fullpath", "test")
.build();
final com.squareup.okhttp.Request request = new com.squareup.okhttp.Request.Builder()
.url(url)
.post(requestBody)
.build();
How can I change the server to read not only the file but also the data?
Can any one help?
Thanks in advance.
The client in this case android is sending additional values in the body like media_type_png. I had to do something similar however the client was angular and not a mobile app, after some searching back then I found code from the following stackoverflow. Which resulted in the code below.
First receive the incoming message and check that you can process it i.e. [IsMimeMultipartContent][1]()
[HttpPost]
public async Task<HttpResponseMessage> Upload()
{
// Here we just check if we can support this
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
// This is where we unpack the values
var provider = new MultipartFormDataMemoryStreamProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
// From the form data we can extract any additional information Here the DTO is any object you want to define
AttachmentInformationDto attachmentInformation = (AttachmentInformationDto)GetFormData(result);
// For each file uploaded
foreach (KeyValuePair<string, Stream> file in provider.FileStreams)
{
string fileName = file.Key;
// Read the data from the file
byte[] data = ReadFully(file.Value);
// Save the file or do something with it
}
}
I used this to unpack the data:
// Extracts Request FormatData as a strongly typed model
private object GetFormData(MultipartFormDataMemoryStreamProvider result)
{
if (result.FormData.HasKeys())
{
// Here you can read the keys sent in ie
result.FormData["your key"]
AttachmentInformationDto data = AttachmentInformationDto();
data.ContentType = Uri.UnescapeDataString(result.FormData["ContentType"]); // Additional Keys
data.Description = Uri.UnescapeDataString(result.FormData["Description"]); // Another example
data.Name = Uri.UnescapeDataString(result.FormData["Name"]); // Another example
if (result.FormData["attType"] != null)
{
data.AttachmentType = Uri.UnescapeDataString(result.FormData["attType"]);
}
return data;
}
return null;
}
The MultipartFormDataMemoryStreamProvider is defined as follows:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
namespace YOURNAMESPACE
{
public class MultipartFormDataMemoryStreamProvider : MultipartMemoryStreamProvider
{
private readonly Collection<bool> _isFormData = new Collection<bool>();
private readonly NameValueCollection _formData = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Stream> _fileStreams = new Dictionary<string, Stream>();
public NameValueCollection FormData
{
get { return _formData; }
}
public Dictionary<string, Stream> FileStreams
{
get { return _fileStreams; }
}
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
if (parent == null)
{
throw new ArgumentNullException("parent");
}
if (headers == null)
{
throw new ArgumentNullException("headers");
}
var contentDisposition = headers.ContentDisposition;
if (contentDisposition == null)
{
throw new InvalidOperationException("Did not find required 'Content-Disposition' header field in MIME multipart body part.");
}
_isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName));
return base.GetStream(parent, headers);
}
public override async Task ExecutePostProcessingAsync()
{
for (var index = 0; index < Contents.Count; index++)
{
HttpContent formContent = Contents[index];
if (_isFormData[index])
{
// Field
string formFieldName = UnquoteToken(formContent.Headers.ContentDisposition.Name) ?? string.Empty;
string formFieldValue = await formContent.ReadAsStringAsync();
FormData.Add(formFieldName, formFieldValue);
}
else
{
// File
string fileName = UnquoteToken(formContent.Headers.ContentDisposition.FileName);
Stream stream = await formContent.ReadAsStreamAsync();
FileStreams.Add(fileName, stream);
}
}
}
private static string UnquoteToken(string token)
{
if (string.IsNullOrWhiteSpace(token))
{
return token;
}
if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
{
return token.Substring(1, token.Length - 2);
}
return token;
}
}
}
I would like to consume my organizations dynamics oData endpoint but with early bound classes. However, there are a lot of early bound tools out there and I wanted to know which one provides the best developer experience/least resistance?
For example, there is this one:
https://github.com/daryllabar/DLaB.Xrm.XrmToolBoxTools
https://github.com/yagasoft/DynamicsCrm-CodeGenerator
and so on. Is there a developer preference/method out there?
Early bound classes are for use with the Organization Service which is a SOAP service. The normal way to generate those classes is using CrmSvcUtil.
OData can be used in Organization Data Service or Web API, but those don't have Early Bound classes.
Further reading: Introducing the Microsoft Dynamics 365 web services
It's not impossible to use with standard SOAP Early bound class. We just have to be creative. If we work just with basic attributes (fields, not relationships, ecc) it seems possible. For example. for create and update, OData will not accept the entire early bounded class, just pass the attibutes:
class Program
{
static void Main(string[] args)
{
string token = System.Threading.Tasks.Task.Run(() => GetToken()).Result;
CRMWebAPI dynamicsWebAPI = new CRMWebAPI("https:/ORG.api.crm4.dynamics.com/api/data/v9.1/",
token);
CRMGetListOptions listOptions = new CRMGetListOptions
{
Select = new string[] { "EntitySetName" },
Filter = "LogicalName eq 'contact'"
};
dynamic entityDefinitions = dynamicsWebAPI.GetList<ExpandoObject>("EntityDefinitions", listOptions).Result;
Contact contact = new Contact
{
FirstName = "Felipe",
LastName = "Test",
MobilePhone = "38421254"
};
dynamic ret = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.Create(entityDefinitions.List[0].EntitySetName, KeyPairValueToObject(contact.Attributes))).Result;
}
public static async Task<string> GetToken()
{
string api = "https://ORG.api.crm4.dynamics.com/";
ClientCredential credential = new ClientCredential("CLIENT_ID", "CLIENT_SECRET");
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/commom/oauth2/authorize");
return authenticationContext.AcquireTokenAsync(api, credential).Result.AccessToken;
}
public static object KeyPairValueToObject(AttributeCollection keyValuePairs)
{
dynamic expando = new ExpandoObject();
var obj = expando as IDictionary<string, object>;
foreach (var keyValuePair in keyValuePairs)
obj.Add(keyValuePair.Key, keyValuePair.Value);
return obj;
}
}
It's a simple approach and I didn't went further.
Maybe we have to serealize other objects as OptionSets, DateTime (pass just the string) and EntityReferences but this simple test worked fine to me. I'm using Xrm.Tools.WebAPI and Microsoft.IdentityModel.Clients.ActiveDirectory. Maybe it's a way.
[Edit]
And so I decided to go and created a not well tested method to cast the attributes. Problems: We have to follow OData statments to use the API. To update/create an entity reference we can use this to reference https://www.inogic.com/blog/2016/02/set-values-of-all-data-types-using-web-api-in-dynamics-crm/
So
//To EntityReference
entityToUpdateOrCreate["FIELD_SCHEMA_NAME#odata.bind"] = "/ENTITY_SET_NAME(GUID)";
So, it's the Schema name, not field name. If you use CamelCase when set you fields name you'll have a problem where. We can resolve that with a (to that cute) code
public static object EntityToObject<T>(T entity) where T : Entity
{
dynamic expando = new ExpandoObject();
var obj = expando as IDictionary<string, object>;
foreach (var keyValuePair in entity.Attributes)
{
obj.Add(GetFieldName(entity, keyValuePair), CastEntityAttibutesValueOnDynamicObject(keyValuePair.Value));
}
return obj;
}
public static object CastEntityAttibutesValueOnDynamicObject(object attributeValue)
{
if (attributeValue.GetType().Name == "EntityReference")
{
CRMGetListOptions listOptions = new CRMGetListOptions
{
Select = new string[] { "EntitySetName" },
Filter = $"LogicalName eq '{((EntityReference)attributeValue).LogicalName}'"
};
dynamic entitySetName = dynamicsWebAPI.GetList<ExpandoObject>("EntityDefinitions", listOptions).Result.List[0];
return $"/{entitySetName.EntitySetName}({((EntityReference)attributeValue).Id})";
}
else if (attributeValue.GetType().Name == "OptionSetValue")
{
return ((OptionSetValue)attributeValue).Value;
}
else if (attributeValue.GetType().Name == "DateTime")
{
return ((DateTime)attributeValue).ToString("yyyy-MM-dd");
}
else if (attributeValue.GetType().Name == "Money")
{
return ((Money)attributeValue).Value;
}
else if (attributeValue.GetType().Name == "AliasedValue")
{
return CastEntityAttibutesValueOnDynamicObject(((AliasedValue)attributeValue).Value);
}
else
{
return attributeValue;
}
}
public static string GetFieldName<T>(T entity, KeyValuePair<string, object> keyValuePair) where T : Entity
{
switch (keyValuePair.Value.GetType().Name)
{
case "EntityReference":
var entityNameList = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.GetEntityDisplayNameList()).Result;
var firstEntity = entityNameList.Where(x => x.LogicalName == entity.LogicalName).FirstOrDefault();
var attrNameList = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.GetAttributeDisplayNameList(firstEntity.MetadataId)).Result;
return attrNameList.Where(x => x.LogicalName == keyValuePair.Key).Single().SchemaName + "#odata.bind";
case "ActivityParty":
throw new NotImplementedException(); //TODO
default:
return keyValuePair.Key;
}
}
Please, note that this approach do not seems fast or good in anyway. It's better if you have all this values as static so we can save some fetches
[Edit 2]
I just found on XRMToolBox a plugin called "Early bound generator for Web API" and it seems to be the best option. Maybe you should give it a try if you're still curious about that. I guess its the best approach.
The final code is this:
static void Main(string[] args)
{
string token = Task.Run(() => GetToken()).Result;
dynamicsWebAPI = new CRMWebAPI("https://ORG.api.crm4.dynamics.com/api/data/v9.1/",
token);
Contact contact = new Contact
{
FirstName = "Felipe",
LastName = "Test",
MobilePhone = "38421254",
new_Salutation = new EntityReference(new_salutation.EntitySetName, new Guid("{BFA27540-7BB9-E611-80EE-FC15B4281C8C}")),
BirthDate = new DateTime(1993, 04, 14),
};
dynamic ret = Task.Run(async () => await dynamicsWebAPI.Create(Contact.EntitySetName, contact.ToExpandoObject())).Result;
Contact createdContact = dynamicsWebAPI.Get<Contact>(Contact.EntitySetName, ret, new CRMGetListOptions
{
Select = new string[] { "*" }
}).Result;
}
and you have to change the ToExpandoObject on Entity.cs class (generated by the plugin)
public ExpandoObject ToExpandoObject()
{
dynamic expando = new ExpandoObject();
var expandoObject = expando as IDictionary<string, object>;
foreach (var attributes in Attributes)
{
if (attributes.Key == GetIdAttribute())
{
continue;
}
var value = attributes.Value;
var key = attributes.Key;
if (value is EntityReference entityReference)
{
value = $"/{entityReference.EntitySetName}({entityReference.EntityId})";
}
else
{
key = key.ToLower();
if (value is DateTime dateTimeValue)
{
var propertyForAttribute = GetPublicInstanceProperties().FirstOrDefault(x =>
x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (propertyForAttribute != null)
{
var onlyDateAttr = propertyForAttribute.GetCustomAttribute<OnlyDateAttribute>();
if (onlyDateAttr != null)
{
value = dateTimeValue.ToString(OnlyDateAttribute.Format);
}
}
}
}
expandoObject.Add(key, value);
}
return (ExpandoObject)expandoObject;
}
Links:
https://github.com/davidyack/Xrm.Tools.CRMWebAPI
https://www.xrmtoolbox.com/plugins/crm.webApi.earlyBoundGenerator/
We currently use XrmToolkit which has it's own version of early binding called ProxyClasses but will allow you to generate early binding using the CRM Service Utility (CrmSvcUtil). It does a lot more than just early binding which is why we use it on all of our projects but the early binding features alone would have me sold on it. in order to regenerate an entity definition all you do is right click the cs file in visual studio and select regenerate and it is done in a few seconds.
For my first 3 years of CRM development I used the XrmToolbox "Early Bound Generator" plugin which is really helpful as well.
I'm trying to port an Android app with a Realm to Xamarin so it'll be also available for iOS devices. In Android, I have several JSON files with some necessary initial data, e.g. cities.json, and I import it at the beginning with realm.createOrUpdateAllFromJson(Class<E> clazz, InputStream in) method, like this:
private void loadInitialCities(Realm realm) {
InputStream stream = context.getAssets().open("data/cities.json");
realm.createOrUpdateAllFromJson(City.class, stream);
}
I also find this method very useful when retrieving data from a web service in form of JSON.
Now with Xamarin I don't see any equivalent to such method. Is there any method to achieve this? Or at least a workaround/tool to create a RealmObject from a JSON in C#?
I wrote my own extension methods for doing this (yes, I miss the built-in helper methods also).
https://github.com/sushihangover/Realm.Json.Extensions
Here is a basic example of how I do it:
JSON Model:
[
{
"name": "Alabama",
"abbreviation": "AL"
},
{
"name": "Alaska",
"abbreviation": "AK"
},
~~~~
]
Realm Model:
public class State : RealmObject
{
public string name { get; set; }
public string abbreviation { get; set; }
}
Xamarin.Android asset and Newtonsoft Streaming reader:
var config = RealmConfiguration.DefaultConfiguration;
config.SchemaVersion = 1;
using (var theRealm = Realm.GetInstance("StackOverflow.realm"))
using (var assetStream = Assets.Open("States.json"))
using (var streamReader = new StreamReader(assetStream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var serializer = new JsonSerializer();
if (!jsonTextReader.Read() || jsonTextReader.TokenType != JsonToken.StartArray)
throw new Exception("Bad Json, start of array missing");
while (jsonTextReader.Read())
{
if (jsonTextReader.TokenType == JsonToken.EndArray)
break;
var state = serializer.Deserialize<State>(jsonTextReader);
theRealm.Write(() =>
{
var realmState = theRealm.CreateObject<State>();
realmState.abbreviation = state.abbreviation;
realmState.name = state.name;
});
}
}
Update: One of my extensions methods:
Extension Method Usage:
using (var theRealm = Realm.GetInstance("StackOverflow.realm"))
using (var assetStream = Assets.Open("States.json"))
{
theRealm.JsonArrayToRealm<State>(assetStream);
}
Extension Method:
Note: This uses AutoMapper to copy RealmObject and avoid reflection, also using Newtonsoft.Json.
public static class RealmDoesJson
{
public static void JsonArrayToRealm<T>(this Realm realm, Stream stream) where T : RealmObject
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<T, T>();
});
using (var streamReader = new StreamReader(stream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var serializer = new JsonSerializer();
if (!jsonTextReader.Read() || jsonTextReader.TokenType != JsonToken.StartArray)
throw new Exception("MALFORMED JSON, Start of Array missing");
while (jsonTextReader.Read())
{
if (jsonTextReader.TokenType == JsonToken.EndArray)
break;
var jsonObject = serializer.Deserialize<T>(jsonTextReader);
realm.Write(() => // inside while loop / single object transaction for memory manangement reasons...
{
var realmObject = realm.CreateObject(typeof(T).Name);
Mapper.Map<T, T>(jsonObject, realmObject);
});
}
}
}
}
I am working on a project using WebApi2. With my test project I am using Moq and XUnit.
So far testing an api has been pretty straight forward to do a GET like
[Fact()]
public void GetCustomer()
{
var id = 2;
_customerMock.Setup(c => c.FindSingle(id))
.Returns(FakeCustomers()
.Single(cust => cust.Id == id));
var result = new CustomersController(_customerMock.Object).Get(id);
var negotiatedResult = result as OkContentActionResult<Customer>;
Assert.NotNull(negotiatedResult);
Assert.IsType<OkNegotiatedContentResult<Customer>>(negotiatedResult);
Assert.Equal(negotiatedResult.Content.Id,id);
}
Now I am moving onto something a little complicated where I need to access value from the request header.
I have created my own Ok() result by extending the IHttpActionResult
public OkContentActionResult(T content,HttpRequestMessage request)
{
_request = request;
_content = content;
}
This allows me to have a small helper that reads the header value from the request.
public virtual IHttpActionResult Post(Customer customer)
{
var header = RequestHeader.GetHeaderValue("customerId", this.Request);
if (header != "1234")
How am I meant to setup Moq with a dummy Request?
I have spent the last hour or so hunting for an example that allows me to do this with webapi however I cant seem to find anything.
So far.....and I am pretty sure its wrong for the api but I have
// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
{ "customerId", "111111" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://foo.com"));
request.Setup(x => x.RawUrl).Returns("/foo");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();
_customerController = new CustomerController()
{
// Request = request,
};
I am not really sure what next I need to do as I havent needed to setup a mock HttpRequestBase in the past.
Can anyone suggest a good article or point me in the right direction?
Thank you!!!
I believe that you should avoid reading the headers in your controller for better separation of concerns (you don't need to read the Customer from request body in the controller right?) and testability.
How I will do it is create a CustomerId class (this is optional. see note below) and CustomerIdParameterBinding
public class CustomerId
{
public string Value { get; set; }
}
public class CustomerIdParameterBinding : HttpParameterBinding
{
public CustomerIdParameterBinding(HttpParameterDescriptor parameter)
: base(parameter)
{
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
actionContext.ActionArguments[Descriptor.ParameterName] = new CustomerId { Value = GetIdOrNull(actionContext) };
return Task.FromResult(0);
}
private string GetIdOrNull(HttpActionContext actionContext)
{
IEnumerable<string> idValues;
if(actionContext.Request.Headers.TryGetValues("customerId", out idValues))
{
return idValues.First();
}
return null;
}
}
Writing up the CustomerIdParameterBinding
config.ParameterBindingRules.Add(p =>
{
return p.ParameterType == typeof(CustomerId) ? new CustomerIdParameterBinding(p) : null;
});
Then in my controller
public void Post(CustomerId id, Customer customer)
Testing the Parameter Binding
public void TestMethod()
{
var parameterName = "TestParam";
var expectedCustomerIdValue = "Yehey!";
//Arrange
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/someUri");
requestMessage.Headers.Add("customerId", expectedCustomerIdValue );
var httpActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = requestMessage
}
};
var stubParameterDescriptor = new Mock<HttpParameterDescriptor>();
stubParameterDescriptor.SetupGet(i => i.ParameterName).Returns(parameterName);
//Act
var customerIdParameterBinding = new CustomerIdParameterBinding(stubParameterDescriptor.Object);
customerIdParameterBinding.ExecuteBindingAsync(null, httpActionContext, (new CancellationTokenSource()).Token).Wait();
//Assert here
//httpActionContext.ActionArguments[parameterName] contains the CustomerId
}
Note: If you don't want to create a CustomerId class, you can annotate your parameter with a custom ParameterBindingAttribute. Like so
public void Post([CustomerId] string customerId, Customer customer)
See here on how to create a ParameterBindingAttribute