Skip to content

Commit

Permalink
Fix 2 separate problems related to incorrect MemberName extraction, f…
Browse files Browse the repository at this point in the history
…or both - MVC and WebAPI. Make a test pushing complete model through WebAPI pipeline to verify no exception is thrown there.
  • Loading branch information
jwaliszko committed Apr 23, 2016
1 parent dc13934 commit 44e2ab7
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private static Assembly LoadAssembly(object sender, ResolveEventArgs args)
assembly.GetType("ExpressiveAnnotations.MvcWebSample.Misc.CustomToolchain").GetMethod("Register").Invoke(null, null); // register your custom methods if you defined any
var attribs = assembly.CompileExpressiveAttributes();

Assert.Equal(29, attribs.Count());
Assert.Equal(30, attribs.Count());
}
catch (ReflectionTypeLoadException e)
{
Expand Down
6 changes: 6 additions & 0 deletions src/ExpressiveAnnotations.MvcWebSample.UITests/HomePage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ public string GetErrorMessage(string id)
return generated.Any() ? generated.Single().Text : elem.Text;
}

public bool SubmitSucceed()
{
var elem = _driver.FindElementByXPath("//div[@class='message-success']");
return "Query successfully submitted.".Equals(elem.Text);
}

public int GetPostbacksCount()
{
var elem = _driver.FindElementByXPath("//meta[@name='postbacks']");
Expand Down
15 changes: 15 additions & 0 deletions src/ExpressiveAnnotations.MvcWebSample.UITests/ServerModeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -638,5 +638,20 @@ public void change_culture_write_a_bit_more_letters_in_comment_field_and_verify_
Home.GetErrorMessage("コメント"));
});
}

[Fact]
public void submit_correct_form_in_server_mode()
{
Watch(() =>
{
Home.ClickCheckbox("GoAbroad");
Home.Select("Age", "20");
Home.WriteInput("BloodType", "A+");
Home.WriteInput("ContactDetails_Phone", "123456789");
Home.ClickRadio("AgreeForContact", "True");
Home.Submit();
Assert.True(Home.SubmitSucceed());
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Threading.Tasks;
using System.Web.Http;
using ExpressiveAnnotations.MvcWebSample.Models;

namespace ExpressiveAnnotations.MvcWebSample.Controllers
{
[System.Web.Http.RoutePrefix("api/Default")]
public class DefaultController : ApiController
{
// POST api/Default/Save
[System.Web.Http.Route("Save")]
public async Task<IHttpActionResult> Save(Query model)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
await Task.Delay(1);
return Ok();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
using System.Web.Mvc;
using ExpressiveAnnotations.MvcWebSample.Models;

Expand All @@ -18,23 +22,38 @@ public ActionResult Index()
LatestSuggestedReturnDate = DateTime.Today.AddMonths(1)
};

Session["Postbacks"] = 0;
Session["Postbacks"] = (int?) TempData["Postbacks"] ?? 0;
ViewBag.Success = TempData["Success"];
return View("Home", TempData["Query"] as Query ?? model);
}

[HttpPost]
public ActionResult Index(Query model)
public async Task<ActionResult> Index(Query model)
{
Session["Postbacks"] = (int)Session["Postbacks"] + 1;
Session["Postbacks"] = (int) Session["Postbacks"] + 1;
if (ModelState.IsValid)
{
TempData["Success"] = "Query successfully submitted";
var result = await Save(model);
if (!result.IsSuccessStatusCode)
throw new ApplicationException("Unexpected failure in WebAPI pipeline.");

TempData["Postbacks"] = Session["Postbacks"];
TempData["Success"] = "Query successfully submitted.";
TempData["Query"] = model;
return RedirectToAction("Index"); // PRG to avoid subsequent form submission attempts on page refresh (http://en.wikipedia.org/wiki/Post/Redirect/Get)
}

return View("Home", model);
}

private async Task<HttpResponseMessage> Save(Query model) // simulate saving through WebAPI call
{
using (var client = new HttpClient())
{
Debug.Assert(Request.Url != null);
client.BaseAddress = new Uri($"{Request.Url.Scheme}://{Request.Url.Authority}/");
return await client.PostAsync("api/Default/Save", model, new JsonMediaTypeFormatter());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{78E4B95D-B0C3-486E-8847-CA444EF5BB95}</ProjectGuid>
<ProjectTypeGuids>{E3E379DF-F4C6-4180-9B81-6769533ABE47};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ExpressiveAnnotations.MvcWebSample</RootNamespace>
Expand Down Expand Up @@ -65,8 +65,8 @@
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http.Formatting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll</HintPath>
<Reference Include="System.Net.Http.Formatting, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.0\lib\net45\System.Net.Http.Formatting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
Expand All @@ -80,12 +80,12 @@
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll</HintPath>
<Reference Include="System.Web.Http, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.0\lib\net45\System.Web.Http.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.Http.WebHost, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll</HintPath>
<Reference Include="System.Web.Http.WebHost, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.0\lib\net45\System.Web.Http.WebHost.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
Expand Down Expand Up @@ -124,6 +124,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\BundleConfig.cs" />
<Compile Include="Controllers\DefaultController.cs" />
<Compile Include="Misc\CustomToolchain.cs" />
<Compile Include="Inheritance\CustomExpressiveAnnotationsModelValidatorProvider.cs" />
<Compile Include="Inheritance\CustomAssertThatAttribute.cs" />
Expand Down
2 changes: 1 addition & 1 deletion src/ExpressiveAnnotations.MvcWebSample/Global.asax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected void Application_Start()

AreaRegistration.RegisterAllAreas();

WebApiConfig.Register(GlobalConfiguration.Configuration);
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Expand Down
5 changes: 4 additions & 1 deletion src/ExpressiveAnnotations.MvcWebSample/Models/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,15 @@ public Query()
[Display(ResourceType = typeof (Resources), Name = "Age")]
public int? Age { get; set; }

[RequiredIf("GoAbroad == true", ErrorMessage = "?")]
public string ID { get; set; }

[RequiredIf("GoAbroad == true",
ErrorMessageResourceType = typeof (Resources), ErrorMessageResourceName = "FieldConditionallyRequired")]
[AssertThat("IsDigitChain(PassportNumber)",
ErrorMessageResourceType = typeof (Resources), ErrorMessageResourceName = "DigitsOnlyAccepted")]
[Display(ResourceType = typeof (Resources), Name = "PassportNumber")]
public string PassportNumber { get; set; }
public string PassportNumber { get; set; }

[Display(ResourceType = typeof (Resources), Name = "Country")]
public string Country { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
</li>
<li>
<pre class="code">
[Required]
</pre>
[Required]
</pre>
<pre class="action">[show attribute]</pre>
<div>
@Html.LabelFor(model => model.Age, new { @class = "inline prefix" })
Expand All @@ -28,6 +28,17 @@
<li>
<pre class="code">
[RequiredIf("GoAbroad == true")]
</pre>
<pre class="action">[show attribute]</pre>
<div>
@Html.LabelFor(model => model.ID, new { @class = "inline prefix" })
@Html.TextBoxFor(model => model.ID)
@Html.ValidationMessageFor(model => model.ID)
</div>
</li>
<li>
<pre class="code">
[RequiredIf("GoAbroad == true")]
[AssertThat("IsDigitChain(PassportNumber)")]
</pre>
<pre class="action">[show attribute]</pre>
Expand Down
15 changes: 7 additions & 8 deletions src/ExpressiveAnnotations.MvcWebSample/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,14 @@
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />


<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers></system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
Expand Down
8 changes: 4 additions & 4 deletions src/ExpressiveAnnotations.MvcWebSample/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.0" targetFramework="net451" />
<package id="Microsoft.AspNet.Razor" version="2.0.20715.0" targetFramework="net451" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.0.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi" version="4.0.20710.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.20710.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.20710.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.20710.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi" version="5.2.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.0" targetFramework="net451" />
<package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net451" />
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.1.1" targetFramework="net451" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net451" />
Expand Down
17 changes: 14 additions & 3 deletions src/ExpressiveAnnotations/Attributes/ExpressiveAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,11 @@ public virtual string FormatErrorMessage(string displayName, string expression,
/// <exception cref="System.ComponentModel.DataAnnotations.ValidationException"></exception>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Debug.Assert(validationContext != null);
Debug.Assert(validationContext != null);

validationContext.MemberName = validationContext.MemberName // in case member name is a null (e.g. like in older MVC versions) try workaround
?? validationContext.ObjectType.GetMemberNameByDisplayName(validationContext.DisplayName);
try
{
AdjustMemberName(validationContext);
return IsValidInternal(value, validationContext);
}
catch (Exception e)
Expand All @@ -277,6 +276,18 @@ protected override ValidationResult IsValid(object value, ValidationContext vali
}
}

private void AdjustMemberName(ValidationContext validationContext)
{
if (validationContext.MemberName != null) // hack for WebAPI, where MemberName is set to display name
validationContext.MemberName = validationContext.ObjectType.GetMemberNameByDisplayName(validationContext.DisplayName);

validationContext.MemberName = validationContext.MemberName // hack for old MVC, where MemberName is not provided:
?? validationContext.ObjectType.GetMemberNameByDisplayName(validationContext.DisplayName) // extract property using display name through Display or DisplayName annotation
?? validationContext.DisplayName; // return DisplayName, which may contain the proper name if no Display nor DisplayName annotation exists

Debug.Assert(validationContext.MemberName != null);
}

private string PreformatMessage(string displayName, string expression, out IList<FormatItem> items)
{
var message = MessageFormatter.FormatString(ErrorMessageString, out items); // process custom format items: {fieldPath[:indicator]}, and substitute them entirely with guids, not to interfere with standard string.Format() invoked below
Expand Down

0 comments on commit 44e2ab7

Please sign in to comment.