模型绑定指的是MVC从浏览器发送的HTTP请求中为我们创建.NET对象,在HTTP请求和C#间起着桥梁的作用。模型绑定的一个最简单的例子是带参数的控制器action方法,比如我们注册这样的路径映射:

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);

控制器Home的Index action带有名为id的参数:

public ActionResult Index(int id) {
Person dataItem = personData.Where(p => p.PersonId == id).First();
return View(dataItem);
}

在我们请求URL“/Home/Index/1”时,默认action调用器ControllerActionInvoker使用模型绑定器为参数id赋值“1”。

默认模型绑定器

模型绑定器实现IModelBinder接口,MVC默认的模型绑定器类名为DefaultModelBinder。它从Request.form、RouteData.Values 、Request.QueryString、Request.Files查找参数值,比如上面例子中的参数id,它在下面路径中搜索:

  1. Request.Form["id"]
  2. RouteData.Values["id"]
  3. Request.QueryString["id"]
  4. Request.Files["id"]

模型绑定器使用参数的名称搜索可用值,一旦找到一个可以结果搜索即停止。

DefaultModelBinder在参数绑定中同时做类型变换,如果类型转换失败,参数绑定也失败,比如我们请求URL “/Home/Index/apple”会得到int类型不能null的错误,模型绑定器无法将apple转换成整数,视图将null赋值给id引发此错误。我们可以定义id参数为int?,这也只能解决部分问题,在Index方法内我们没有检查id为null的情况,我们可以使用默认参数来彻底解决:

...
public ActionResult Index(int id = 1) {
Person dataItem = personData.Where(p => p.PersonId == id).First();
return View(dataItem);
}
...

实际的应用中我们还需要验证绑定的参数值,比如URL  /Home/Index/-1和 /Home/Index/500都可以成功绑定数值到id,但他们超过了集合的上下限。在类型转换时还必须注意文化语言差异,比如日期格式,我们可以使用语言无关的通用格式yyyy-mm-dd。

复杂类型的绑定

上面我们看到的都是绑定到简单c#类型的例子,如果要绑定的模型是类则要复杂的多。以下面的Model类为例:

 
 public class Person {
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public Address HomeAddress { get; set; }
public bool IsApproved { get; set; }
public Role Role { get; set; }
} public class Address {
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
} public enum Role {
Admin,
User,
Guest
}
 

创建两个CreatePerson控制器action来获取数据:

 
public ActionResult CreatePerson() {
  return View(new Person());
}
[HttpPost]
public ActionResult CreatePerson(Person model) {
  return View("Index", model);
}
 

这里的action方法参数为复杂类型Person,我们使用Html.EditorFor()帮助函数在视图中创建输入数据的HTML:

 
@model MvcModels.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>Create Person</h2>
@using (Html.BeginForm())
{
<div>@Html.LabelFor(m => m.PersonId)@Html.EditorFor(m => m.PersonId)</div>
<div>@Html.LabelFor(m => m.FirstName)@Html.EditorFor(m => m.FirstName)</div>
<div>@Html.LabelFor(m => m.LastName)@Html.EditorFor(m => m.LastName)</div>
<div>@Html.LabelFor(m => m.Role)@Html.EditorFor(m => m.Role)</div>
<div>
@Html.LabelFor(m => m.HomeAddress.City)
@Html.EditorFor(m => m.HomeAddress.City)
</div>
<div>
@Html.LabelFor(m => m.HomeAddress.Country)
@Html.EditorFor(m => m.HomeAddress.Country)
</div>
<button type="submit">Submit</button>
}
 

使用强类型的EditFor函数能保证生成的HTML元素Name包含模型绑定需要的嵌套前缀,比如HomeAddress.Country,生成的HTML为:

...
<input class="text-box single-line" id="HomeAddress_Country" name="HomeAddress.Country" type="text" value="" />
...

自定义绑定名称前缀

有这样一种情况,我们根据一个对象类型生成HTML,但是希望结果绑定到另外一个对象类型,我们可以通过自定义绑定前缀来实现。比如我们的Model类:

public class AddressSummary {
public string City { get; set; }
public string Country { get; set; }
}

定义一个控制器方法来使用这个Model:

public ActionResult DisplaySummary(AddressSummary summary) {
return View(summary);
}

对应的DisplaySummary.cshtml视图也使用这个Model类:

 
@model MvcModels.Models.AddressSummary
@{
ViewBag.Title = "DisplaySummary";
}
<h2>Address Summary</h2>
<div><label>City:</label>@Html.DisplayFor(m => m.City)</div>
<div><label>Country:</label>@Html.DisplayFor(m => m.Country)</div>
 

如果我们从上面编辑Person的视图CreatePerson.cshtml提交到DisplaySummary action:

 
@model MvcModels.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>Create Person</h2>
@using(Html.BeginForm("DisplaySummary", "Home")) {
<div>@Html.LabelFor(m => m.PersonId)@Html.EditorFor(m=>m.PersonId)</div>
<div>@Html.LabelFor(m => m.FirstName)@Html.EditorFor(m=>m.FirstName)</div>
<div>@Html.LabelFor(m => m.LastName)@Html.EditorFor(m=>m.LastName)</div>
<div>@Html.LabelFor(m => m.Role)@Html.EditorFor(m=>m.Role)</div>
<div>
@Html.LabelFor(m => m.HomeAddress.City)
@Html.EditorFor(m=> m.HomeAddress.City)
</div>
<div>
@Html.LabelFor(m => m.HomeAddress.Country)
@Html.EditorFor(m=> m.HomeAddress.Country)
</div>
<button type="submit">Submit</button>
}
 

DisplaySummary视图中将无法正确绑定City和Country,因为CreatePerson中City和Country的input元素名称包含HomeAddress前缀,提交的数据是HomeAddress.City和HomeAddress.Country,而DisplaySummary视图中是不需要这个前缀的。我们可以在控制器方法上通过Bind特性指定绑定前缀来修正:

public ActionResult DisplaySummary([Bind(Prefix="HomeAddress")]AddressSummary summary) {
return View(summary);
}

在Bind特性中我们还可以指定哪个属性不要绑定,比如:

public ActionResult DisplaySummary([Bind(Prefix="HomeAddress", Exclude="Country")]AddressSummary summary) {
return View(summary);
}

这里通过Exclude="Country"禁止Country属性的绑定,与此相对,可以通过Include来指定需要绑定的属性。Bind可以应用在单个action方法上,如果需要更大范围的效果,我们可以直接应用在模型类上:

[Bind(Include="City")]
public class AddressSummary {
public string City { get; set; }
public string Country { get; set; }
}

Bind可以同时应用在Model类和action方法上,一个属性只有在两个地方都没有被排除才会包含在绑定结果中。

绑定到数组和集合

DefaultModelBinder支持数组集合的绑定,比如下面的action方法使用数组作为参数:

public ActionResult Names(string[] names) {
names = names ?? new string[0];
return View(names);
}

视图中我们创建一组同名的input元素:

 
@model string[]
@{
ViewBag.Title = "Names";
}
<h2>Names</h2>
@if (Model.Length == 0) {
using(Html.BeginForm()) {
for (int i = 0; i < 3; i++) {
<div><label>@(i + 1):</label>@Html.TextBox("names")</div>
}
<button type="submit">Submit</button>
}
} else {
foreach (string str in Model) {
<p>@str</p>
}
@Html.ActionLink("Back", "Names");
}
 

生成的HTML:

 
...
<form action="/Home/Names" method="post">
<div><label>1:</label><input id="names" name="names"type="text" value="" /></div>
<div><label>2:</label><input id="names" name="names"type="text" value="" /></div>
<div><label>3:</label><input id="names" name="names"type="text" value="" /></div>
<button type="submit">Submit</button>
</form>
...
 

提交数据时绑定器从多个names构建一个数组。

上面的例子换成集合是这样的:

public ActionResult Names(IList<string> names) {
names = names ?? new List<string>();
return View(names);
}

视图:

 
@model IList<string>
@{
ViewBag.Title = "Names";
}
<h2>Names</h2>
@if (Model.Count == 0) {
using(Html.BeginForm()) {
for (int i = 0; i < 3; i++) {
<div><label>@(i + 1):</label>@Html.TextBox("names")</div>
}
<button type="submit">Submit</button>
}
} else {
foreach (string str in Model) {
<p>@str</p>
}
@Html.ActionLink("Back", "Names");
}
 

如果是要绑定到一个自定义Model类型的集合:

public ActionResult Address(IList<AddressSummary> addresses) {
addresses = addresses ?? new List<AddressSummary>();
return View(addresses);
}

视图:

 
@using MvcModels.Models
@model IList<AddressSummary>
@{
ViewBag.Title = "Address";
}
<h2>Addresses</h2>
@if (Model.Count() == 0) {
using (Html.BeginForm()) {
for (int i = 0; i < 3; i++) {
<fieldset>
<legend>Address @(i + 1)</legend>
<div><label>City:</label>@Html.Editor("[" + i + "].City")</div>
<div><label>Country:</label>@Html.Editor("[" + i + "].Country")</div>
</fieldset>
}
<button type="submit">Submit</button>
}
} else {
foreach (AddressSummary str in Model) {
<p>@str.City, @str.Country</p>
}
@Html.ActionLink("Back", "Address");
}
 

生成的HTML表单:

 
...
<fieldset>
<legend>Address 1</legend>
<div>
<label>City:</label>
<input class="text-box single-line" name="[0].City"type="text" value="" />
</div>
<div>
<label>Country:</label>
<input class="text-box single-line" name="[0].Country"type="text" value="" />
</div>
</fieldset>
<fieldset>
<legend>Address 2</legend>
<div>
<label>City:</label>
<input class="text-box single-line" name="[1].City"type="text" value="" />
</div>
<div>
<label>Country:</label>
<input class="text-box single-line" name="[1].Country"type="text" value="" />
</div>
</fieldset>
...
 

使用[0]、[1]作为输入元素的名称前缀,绑定器知道需要创建一个集合。

手工调用模型绑定

在请求action方法时MVC自动为我们处理模型绑定,但是我们也可以在代码中手工绑定,这提供了额外的灵活性。我们调用控制器方法UpdateModel手工绑定:

public ActionResult Address() {
IList<AddressSummary> addresses = new List<AddressSummary>();
UpdateModel(addresses);
return View(addresses);
}

我们可以提供UpdateModel额外的参数指定要数据提供者:

public ActionResult Address() {
IList<AddressSummary> addresses = new List<AddressSummary>();
UpdateModel(addresses, new FormValueProvider(ControllerContext));
return View(addresses);
}

参数FormValueProvider指定从Request.Form绑定数据,其他可用的Provider的还有RouteDataValueProvider(RouteData.Values)、QueryStringValueProvider(Request.QueryString)、HttpFileCollectionValueProvider(Request.Files),它们都实现IValueProvider接口,使用控制器类提供的ControllerContext作为构造函数参数。

实际上最常用的限制绑定源的方式是:

public ActionResult Address(FormCollection formData) {
IList<AddressSummary> addresses = new List<AddressSummary>();
UpdateModel(addresses, formData);
return View(addresses);
}

FormCollection为表单数据的键值集合,这是UpdateModel众多重载形式中的一种。

手工数据绑定的另外一个好处是方便我们处理绑定错误:

 
public ActionResult Address(FormCollection formData) {
IList<AddressSummary> addresses = new List<AddressSummary>();
try {
UpdateModel(addresses, formData);
} catch (InvalidOperationException ex) {
// provide feedback to user
}
return View(addresses);
}
 

另外一种处理错误的方式是使用TryUpdateModel:

 
public ActionResult Address(FormCollection formData) {
IList<AddressSummary> addresses = new List<AddressSummary>();
if (TryUpdateModel(addresses, formData)) {
// proceed as normal
} else {
// provide feedback to user
}
return View(addresses);
}
 

自定义Value Provider

除了上面看到的内建Value provider,我们可以从IValueProvider接口实现自定义的Value provider:

namespace System.Web.Mvc {
public interface IValueProvider {
bool ContainsPrefix(string prefix);
ValueProviderResult GetValue(string key);
}
}

模型绑定器调用ContainsPrefix方法确定value provider是否可以处理提供的名称前缀,GetValue根据传入的键返回可用的参数值,如果没有可用的数据返回null。下面用实例演示如何使用自定义value provider:

 
public class CountryValueProvider : IValueProvider {

        public bool ContainsPrefix(string prefix) {
return prefix.ToLower().IndexOf("country") > -1;
} public ValueProviderResult GetValue(string key) {
if (ContainsPrefix(key)) {
return new ValueProviderResult("USA", "USA", CultureInfo.InvariantCulture);
} else {
return null;
}
}
}

CountryValueProvider处理任何包含country的属性,对所有包含country名称的属性总是返回“USA”。使用自定义value provider之前还需要创建一个工厂类来创建自动那个有value provider的实例:

public class CustomValueProviderFactory : ValueProviderFactory {

        public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
return new CountryValueProvider();
}
}

最后把我们的类工厂在global.asax的application_start中添加到value provider工厂列表中:

 
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
AreaRegistration.RegisterAllAreas(); ValueProviderFactories.Factories.Insert(0, new CustomValueProviderFactory()); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
 

这里使用ValueProviderFactories.Factories.Insert()将自定义的value provider工厂添加到列表首位以优先使用,当然也可以ValueProviderFactories.Factories.Add()添加到列表末尾。在注册使用这个value provider后,任何对country属性的绑定都会得到值USA。

自定义模型绑定器

除了自定义value provider,我们还可以从IModelBinder接口创建自定义的模型绑定器:

 
public class AddressSummaryBinder : IModelBinder {

        public object BindModel(ControllerContext controllerContext,  ModelBindingContext bindingContext) {

            AddressSummary model = (AddressSummary)bindingContext.Model ?? new AddressSummary();
model.City = GetValue(bindingContext, "City");
model.Country = GetValue(bindingContext, "Country");
return model;
} private string GetValue(ModelBindingContext context, string name) {
name = (context.ModelName == "" ? "" : context.ModelName + ".") + name; ValueProviderResult result = context.ValueProvider.GetValue(name);
if (result == null || result.AttemptedValue == "") {
return "<Not Specified>";
} else {
return (string)result.AttemptedValue;
}
} }
 

MVC调用AddressSummaryBinder的BindModel()方法获取模型类型的实例,这里简单的初始化一个AddressSummary实例,调用value provider获取对象属性值,在从value provider获取属性值时我们把添加模型名称ModelBindingContext.ModelName作为属性的前缀。同样,必须在application_start中注册自定义模型绑定器后才能使用:

...
ModelBinders.Binders.Add(typeof(AddressSummary), new AddressSummaryBinder());
...

Dependency Injection和依赖解决器

C#中使用接口可以帮助我们解耦构件, 获取接口的实现我们通常是直接初始化接口的一个实现类:

 
public class PasswordResetHelper {
  public void ResetPassword() {
    IEmailSender mySender = new MyEmailSender();
    //...call interface methods to configure e-mail details...
    mySender.SendEmail();
    }
}
 

使用IEmailSender接口在一定程度上PasswordResetHelper不再要求发送邮件时需要一个具体的邮件发送类,但是直接初始化MyEmailSender使得PasswordResetHelper并没有和MyEmailSender解耦开。我们可以把IEmailSender接口的初始化放到PasswordResetHelper的构造函数上来解决:

 
public class PasswordResetHelper {
  private IEmailSender emailSender;
  public PasswordResetHelper(IEmailSender emailSenderParam) {
    emailSender = emailSenderParam;
  }
  public void ResetPassword() {
    // ...call interface methods to configure e-mail details...
    emailSender.SendEmail();
  }
}
 

但这样带来的问题是如何获取IEmailSender的实现呢?这可以通过运行时Dependency Injection机制来解决,在创建PasswordResetHelper实例时依赖解决器提供一个IEmailSender的实例给PasswordResetHelper构造函数,这种注入方式又称为构造注入。依赖解决器又是怎么知道如何初始化接口的固实实现呢?答案是DI容器,通过在DI容器中注册接口/虚类和对应的实现类将两者联系起来。当然DI不只是DI容器这么简单,还必须考虑类型依赖链条、对象生命周期管理、构造函数参数配置等等问题,好在我们不需要编写自己的容器,微软提供自己的DI容器名为Unity(在nity.codeplex.com获取),而开源的Ninject是个不错的选择。Ninject可以在visual studio中使用nuget包管理器获取并安装,下面就以实例演示如何使用Ninject,我们从接口的定义开始:

 
using System.Collections.Generic;

namespace EssentialTools.Models {
public interface IValueCalculator { decimal ValueProducts(IEnumerable<Product> products);
}
}
 

接口的一个类实现:

 
using System.Collections.Generic;
using System.Linq; namespace EssentialTools.Models { public class LinqValueCalculator : IValueCalculator {
private IDiscountHelper discounter; public LinqValueCalculator(IDiscountHelper discounterParam) {
discounter = discounterParam;
} public decimal ValueProducts(IEnumerable<Product> products) {
return discounter.ApplyDiscount(products.Sum(p => p.Price));
}
}
}
 

我们创建一个使用Ninject的自定义依赖解决器:

 
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Ninject;
using EssentialTools.Models; namespace EssentialTools.Infrastructure {
public class NinjectDependencyResolver : IDependencyResolver {
private IKernel kernel; public NinjectDependencyResolver() {
kernel = new StandardKernel();
AddBindings();
} public object GetService(Type serviceType) {
return kernel.TryGet(serviceType);
} public IEnumerable<object> GetServices(Type serviceType) {
return kernel.GetAll(serviceType);
} private void AddBindings() {
kernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); }
}
}
 

这里最重要的是AddBindings方法中的kernel.Bind<IValueCalculator>().To<LinqValueCalculator>(),它将接口IValueCalculator和类实现LinqValueCalculator结合起来,在我们需要接口IValueCalculator的一个实例时,会调用NinjectDependencyResolver的GetService获取到LinqValueCalculator的一个实例。要使NinjectDependencyResolver起作用还必须注册它为应用默认的依赖解决器,这是在application_start中操作:

 
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
AreaRegistration.RegisterAllAreas(); DependencyResolver.SetResolver(new NinjectDependencyResolver()); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes); }
}
 

控制器的构造函数中我们传入接口IValueCalculator,依赖解决器会自动为我们创建一个LinqValueCalculator的实例:

 
public class HomeController : Controller {
private Product[] products = {
new Product {Name = "Kayak", Category = "Watersports", Price = 275M},
new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M},
new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M},
new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M}
};
private IValueCalculator calc; public HomeController(IValueCalculator calcParam) {
calc = calcParam;
} public ActionResult Index() {
ShoppingCart cart = new ShoppingCart(calc) { Products = products }; decimal totalValue = cart.CalculateProductTotal(); return View(totalValue);
}
}
 

Ninject的绑定方法非常的灵活:

kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50M); //绑定时指定DefaultDiscountHelper的属性DiscountSize=50
kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);//绑定时指定DefaultDiscountHelper的构造函数参数discountParam=50
kernel.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>();//条件绑定,在注入到LinqValueCalculator时绑定接口LinqValueCalculator到FlexibleDiscountHelper

除了使用自定义的依赖解决器,我们可以从默认控制器工厂扩展控制器工厂,在自定义控制器工厂中使用Ninject依赖注入:

 
 public class NinjectControllerFactory : DefaultControllerFactory {
private IKernel ninjectKernel; public NinjectControllerFactory() {
ninjectKernel = new StandardKernel();
AddBindings();
} protected override IController GetControllerInstance(RequestContext
requestContext, Type controllerType) { return controllerType == null
? null
: (IController)ninjectKernel.Get(controllerType);
} private void AddBindings() {
ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
}
 

MVC在获取控制器时调用GetControllerInstance,它使用ninjectKernel.Get(controllerType)来获取相应的控制类实例,同时解决构造注入的问题,比如HomeController的构造函数参数IValueCalculator calcParam,使用这种方式可以限制仅在控制器内注入,控制器外整个应用范围内我们仍然可以使用自定义依赖解决器注入。

需要注意的是依赖解决和注入不是模型绑定的一部分,但它们有一定的相似性,后者解决的action方法上的参数绑定,前者可以说是整个控制器类(构造函数)上的参数绑定(当然不只是用在控制器类上)。

ASP.NET MVC 模型绑定的更多相关文章

  1. ASP.NET MVC模型绑定的6个建议(转载)

    ASP.NET MVC模型绑定的6个建议 发表于2011-08-03 10:25| 来源博客园| 31 条评论| 作者冠军 validationasp.netmvc.netasp 摘要:ASP.NET ...

  2. ASP.NET没有魔法——ASP.NET MVC 模型绑定

    在My Blog中已经有了文章管理功能,可以发布和修改文章,但是对于文章内容来说,这里缺少最重要的排版功能,如果没有排版的博客很大程度上是无法阅读的,由于文章是通过浏览器查看的,所以文章的排版其实与网 ...

  3. ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(下篇)

    上一篇<ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)>文章介绍了ASP.NET MVC模型绑定的相关组件和概念,本章将介绍Controller在执行时是如何通过这 ...

  4. asp.net Mvc 模型绑定项目过多会导致页面运行时间卡

    asp.net Mvc 模型绑定项目过多会导致页面运行时间卡的问题. 解决方式就是采用ModelView方式进行精简,已减少模型绑定及验证的时间.

  5. [转] ASP.NET MVC 模型绑定的功能和问题

    摘要:本文将与你深入探究 ASP.NET MVC 模型绑定子系统的核心部分,展示模型绑定框架的每一层并提供扩展模型绑定逻辑以满足应用程序需求的各种方法. 同时,你还会看到一些经常被忽视的模型绑定技术, ...

  6. ASP.NET MVC——模型绑定

    这篇文章我们来讲讲模型绑定(Model Binding),其实在初步了解ASP.NET MVC之后,大家可能都会产生一个疑问,为什么URL片段最后会转换为例如int型或者其他类型的参数呢?这里就不得不 ...

  7. ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)

    前面文章介绍了ASP.NET MVC中的模型绑定和验证功能,本着ASP.NET MVC没有魔法的精神,本章内容将从代码的角度对ASP.NET MVC如何完成模型的绑定和验证进行分析,已了解其原理. 本 ...

  8. ASP.NET MVC模型绑定1

    一.模型绑定原理 模型绑定是指为Controller的Action方法的参数提供值的过程,例如我有一个名为Blog的实体类(准确的说是ViewModel),它有一个名为Title的属性,如果我在VIE ...

  9. 禁止ASP.NET MVC模型绑定时将空字符串绑定为null

    为model添加[DisplayFormat(ConvertEmptyStringToNull = false)] [Display(ResourceType = typeof(AppStrings) ...

随机推荐

  1. 动态加载与插件系统的初步实现(四):解析JSON、扩展Fiddler

    按文章结构,这部分应该给出WCFRest项目示例,我想WinForm示例足够详尽了,况且WCFRest还不需要使用插件AppDomain那一套,于是把最近写的Fiddler扩展搬上来吧. Fiddle ...

  2. 黑白表格样式教师求职简历免费word模板

    10款精黑白表格样式教师求职简历免费word模板,也可用于其他专业和职业,个人免费简历模板,个人简历表免费,个人简历表格. 声明:该简历模板仅用于个人欣赏使用,请勿用于商业用途,谢谢. 下载地址:百度 ...

  3. 自动化jenkins报:ModuleNotFoundError: No module named 'common'

    直接执行脚本是没有问题,报如下错误: 你已经在run.py脚本加路径了为什么还会报这个错呢,就是你加的路径,应该在所有的包上面,才不会报这个错,如下: 注:以下是我的解决方法仅作参考.如果我的发表的内 ...

  4. CentOS查看一共安装了多少软件包,是那些软件包

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48292853 一.如何得知共安装了多少个软件包 [root@localhost ~ ...

  5. Python 3 利用机器学习模型 进行手写体数字检测

    0.引言 介绍了如何生成手写体数字的数据,提取特征,借助 sklearn 机器学习模型建模,进行识别手写体数字 1-9 模型的建立和测试. 用到的几种模型: 1. LR,Logistic Regres ...

  6. VMware vSphere 6.0 安装及管理手册

    目录 1. VMWARE_VSPHERE安装 1.1. 底层ESXI 安装步骤 1.2. VCENTER安装步骤 1) 准备vCenter安装环境 2) vCenter安装步骤 2. VMWARE_V ...

  7. centos7.6 安装配置rabbitmq

    IP地址:192.168.200.108 安装erlang 和 依赖环境 yum install -y socat yum install -y erlang 安装rabbitmq yum insta ...

  8. ubuntu下import matplotlib错误解决办法

    环境:ubuntu16.04,python2.7,tensorflow1.4.0 问题: ImportError: No moudule named _tkinter, please install ...

  9. vue+webpack前端开发项目的安装方法

    安装前,需要进行node.npm检测,查看是否已有安装node.npm环境: 操作方法:Windows+R 调出运行框,输入cmd 调出命令框:分别输入node -v 回车(查看node版本) npm ...

  10. mongodb基本使用(三)

    MongoDB 创建数据库 语法 MongoDB 创建数据库的语法格式如下: use DATABASE_NAME 如果数据库不存在,则创建数据库,否则切换到指定数据库. 如果你想查看所有数据库,可以使 ...