ASP.NET WEB API

与WEB API有关的类型

HttpMessageHandler(System.Net.Http)(消息处理器)

表示Http请求的处理程序,处理程序类似于Http管道,它们是链式调用,所以可以自定义更多的处理程序。

HttpClient(System.Net.Http)(Http客户端)

表示客户端请求的类,可以配置请求的WEB API地址、Http报头、异步发送请求和读取服务端响应的Http报文等操作。HttpClient默认的构造函数就是利用HttpMessageHandler处理Http请求,而开发人员也可以在初始化HttpClient的时候向其构造函数传递一个自定义的消息处理器。

BaseAddress
//请求的地址主机名和端口号或域名

DefaultRequestHeaders
//请求报头的集合,提供了Add方法用于添加报头,报头以键值对的方式添加
//示例:
request.DefaultRequestHeaders.Add( "Accept", "application/json" );
//另一种方式是先创建报头对象,然后像下面这样添加:
request.DefaultRequestHeaders.Accept.Add( new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue( "application/json" ) ); //MediaTypeWithQualityHeaderValue表示Http报文的Accept请求头,更多报头可参考MSDN。

GetAsync( string uri )
//异步发起对WEB API的调用,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>

PostAsJsonAsync( string uri, T value )
//将value序列化为json字符串,异步提交Post请求到参数指定的地址,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>
//示例:
Product product = new Product { Name = "Hex", Category = "音乐", Price =  };
HttpResponseMessage response = request.PostAsJsonAsync( "/api/products", product ).Result; //调用Task的Result属性提取异步任务的返回值,这会阻塞所有线程直到异步任务成功返回数据

PutAsJsonAsync( )
//异步修改数据,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>
//示例:
Product product = new Product { Name = "万有引力之虹", Category = "图书", Price =  };
HttpResponseMessage response = request.PutAsJsonAsync( "/api/products/1", product ).Result; //服务端接收一个产品id和一个Product实体

DeleteAsync( )
//异步删除数据,此方法会自动开启一个新的线程Task,返回一个Task<HttpResponseMessage>

HttpResponseMessage(System.Net.Http)(Http响应流)

表示服务端返回的消息

IsSuccessStatusCode
//获取Http请求是否成功返回了状态码,状态码在200-299之间时返回true

StatusCode
//获取或设置服务端返回的Http状态码,(int)response.StatusCode

ReasonPhrase
//获取或设置服务端返回的Http状态码相关的信息

Content
//获取或设置报文主体,即服务端返回的数据,返回一个HttpContent,可以实例化一个StringContent来创建响应的内容,因为StringContent从HttpContent派生

 
Headers
//获取服务端返回的头部信息集合,可通过在其上调用GetValues(string headerKey)来获取请求头信息

HttpRequestMessage(System.Net.Http)(Http请求流)

表示客户端请求的消息

CreateResponse( HttpStatusCode code, T value )
//创建一个响应流对象,value表示报文主体内容

Content
//获取或设置报文主体,即客户端提交的数据,返回一个HttpContent

Headers
//获取客户端请求的头部信息集合,可通过在其上调用GetValues(string headerKey)来获取请求头信息

HttpContent(System.Net.Http)(Http报文主体)

表示客户端或服务端发送的报文主体内容

ReadAsAsync
//异步读取报文数据,返回一个Task,要取出数据需要Task.Result
//示例:
string json = response.Content.ReadAsAsync<string>( ).Result; //读取http报文

Headers
//报文主体内容的头信息集合,可通过在其上调用GetValues(string headerKey)来获取报文内容中的头信息,比如获取Content-Type
//示例:
IEnumerable<string> contentType = response.Content.Headers.GetValues( "Content-Type" );
foreach(var str in contentType)
{
    Console.WriteLine( str);
}

创建ASP.NET WEB API服务

选择ASP.NET WEB应用程序,勾选WEB API

或选择空,这样就可以取消勾选MVC,值创建一个不包含MVC的API项目:

项目创建完成后可以看到Controllers目录有两个控制器,一个用于ASP.NET MVC,另一个ValueController就是用于API服务的API控制器,每一个API控制器都从ApiController派生。Api控制器的方法返回类型按约定最好是使用HttpResponseMessage,每个方法按约定的前缀名称定义,Get前缀是获取数据,Post前缀是以Post提交方式提交数据,Put前缀是修改数据,Delete前缀是删除数据,按照这四种约定来定义你的Http处理函数即可。而客户端在调用API服务时,其请求的地址不能包含处理Http请求的函数名称,只包含Api控制器的名称即可,因为客户端会使用HttpClient的以Get、Post、Put、Delete作为前缀的函数名来发起Http请求,所以服务端会自动根据前缀约定找到Api控制器下对应的处理函数来处理请求。

配置Web.Config允许其它服务端返回的页面使用Ajax跨域请求

<system.webServer>
     <httpProtocol>
          <customHeaders>
               <add name="Access-Control-Allow-Origin" value="*" />
               <add name="Access-Control-Allow-Headers" value="*" />
               <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
          </customHeaders>
     </httpProtocol>
</system.webServer>

首先需要在Models目录定义模型类、处理模型的接口、处理模型的类型,它们分别是Product、IProductRepository、ProductRepository。

namespace API.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

Product

namespace API.Models
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetAll( );
        Product Get( int id );
        Product Add( Product item );
        void Remove( int id );
        bool Update( Product item);             
    }
}

IProductRepository

namespace API.Models
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> products = new List<Product>( );
        private int nextId = ;
        public ProductRepository( )
        {
            Add( new Product { Id = nextId++, Category = "图书", Name = "寂静的春天", Price =  } );
            Add( new Product { Id = nextId++, Category = "音乐", Name = "King Of Sweet", Price =  } );
            Add( new Product { Id = nextId++, Category = "音乐", Name = "Sweet Glow Of Silence", Price =  } );
            Add( new Product { Id = nextId++, Category = "图书", Name = "精神分析导论", Price =  } );
            Add( new Product { Id = nextId++, Category = "音乐", Name = "Zebra", Price =  } );
        }

//根据id查询产品
        public Product Get( int id )
        {
            return products.Single( p => p.Id == id );
        }

//查询所有产品
        public IEnumerable<Product> GetAll( )
        {
            return products;
        }

//添加产品
        public Product Add( Product item )
        {
            item.Id = nextId++;
            products.Add( item );
            return item;
        }

//删除产品
        public void Remove( int id )
        {
            products.RemoveAt( id );
        }

//修改产品
        public bool Update( Product item )
        {
            var product=products.Single( p => p.Id == item.Id );
            products.Remove( product );
            products.Add( item );
            return true;
        }
    }
}

ProductRepository

新建一个Api控制器

using API.Models;
using Newtonsoft.Json;

namespace API.Controllers
{
    public class ProductsController : ApiController
    {
        static readonly IProductRepository productRepository = new ProductRepository( );

//查询所有产品
        public HttpResponseMessage GetAll( )
        {
            return Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( productRepository.GetAll( ) ) );
        }

//根据id查询产品
        public HttpResponseMessage GetProduct( int id )
        {
            Product item = productRepository.Get( id );
            return item == null ? throw new HttpResponseException( HttpStatusCode.NotFound ) : Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( item ) );
        }

//根据分类查询产品
        public HttpResponseMessage GetProductCategory( string category )
        {
            var list = productRepository.GetAll( ).Where( p => p.Category == category );
            return Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( list ) );
        }

//添加产品
        public HttpResponseMessage PostProduct( Product item )
        {
            item = productRepository.Add( item );
            var response = Request.CreateResponse( HttpStatusCode.Created, item );
            string uri = Url.Link( "DefaultApi", new { id = item.Id } );
            response.Headers.Location = new Uri( uri );
            return response;
        }

//修改产品
        public HttpResponseMessage PutProduct( int id, Product product )
        {
            product.Id = id;
            bool update = productRepository.Update( product );
            return Request.CreateResponse( HttpStatusCode.OK );
        }

//删除产品
        public HttpResponseMessage DeleteProduct( int id )
        {
            productRepository.Remove( id );
            return new HttpResponseMessage( HttpStatusCode.NoContent ); //删除后可以返回http204以表示再无此条目
        }
    }
}

WEB API默认返回xml的数据,为了能返回json格式,可通过修改App_Start目录的WebApiConfig.cs文件,在Register方法中作如下配置:

using System.Net.Http.Formatting;
using Newtonsoft.Json.Serialization;

namespace API
{
    public static class WebApiConfig
    {
        public static void Register( HttpConfiguration config )
        {
            // Web API 配置和服务
            // Web API configuration and services
            var json = config.Formatters.JsonFormatter;
            // 解决json序列化时的循环引用问题
            json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            // 干掉XML序列化器
            config.Formatters.Remove( config.Formatters.XmlFormatter );

var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>( ).First( );
            jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver( );
        }
    }
}

新建一个CUI程序来表示客户端

using System.Net.Http;
using Newtonsoft.Json;

namespace ClientCallWebAPI
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }

class Program
    {
        static void Main( string[] args )
        {
            HttpClient request = new HttpClient( ); //创建发送请求的http对象
            request.BaseAddress = new Uri( "http://localhost:54838" ); //请求的地址主机名和端口号或域名        
            request.DefaultRequestHeaders.Add( "Accept", "application/json" );//添加Accept报头,定义我能接受的从服务端返回的数据类型
            //发起异步请求,等待返回一个http报文
            //request.GetAsync( "/api/products/1" );  根据id查询产品
            //request.GetAsync( "/api/products" );  查询所有产品
            //request.GetAsync( "/api/products?category=图书" ); 根据类别查询产品 
            HttpResponseMessage response = request.GetAsync( "/api/products?category=图书" ).Result; 
            if (!response.IsSuccessStatusCode) Console.WriteLine( "服务端无响应" );
            string json= response.Content.ReadAsAsync<string>( ).Result; //读取http报文
           
            IEnumerable<Product> products = JsonConvert.DeserializeObject<IEnumerable<Product>>( json );
            if (!products.Any( ))
            {
                Console.WriteLine( $"{response.StatusCode}{response.ReasonPhrase}" );
                return;
            }
            foreach (var item in products)
            {
                Console.WriteLine( $"{item.Id}   {item.Name}   {item.Category}   {item.Price}" );
            }
        }
    }
}

例子中调用HttpClient的异步操作方法向服务端发起请求,而在WEB应用程序中,除了可以使用Result阻塞所有线程等待异步任务完成以外,还可以使用await操作符达到同样的效果,如:

public HttpResponseMessage Index()
{    
    HttpResponseMessage response = request.GetAsync( "/api/products?category=图书" ).Result;      
}

或:

public async Task<HttpResponseMessage > Index()
{  
    HttpResponseMessage response =await request.GetAsync( "/api/products?category=图书" );
}

API控制器的Action方法也可以直接返回string,如:

//FromBody特性应用在参数上,表示将客户端发送的Http报文主体的数据转换为C#实体模型
public string Add( [FromBody] Product pro )
{
    return $"{pro.Name},{pro.Category},{pro.Price}";
}

添加API路由模板

默认情况下Http请求调用API的Url都是以api开头,带api控制器名,但不带Action名称,这是在WebApiConfig.cs中默认的路由模板所定义的,你可以更改这个模板,以便可以像下面那样调用API

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}", //没有action
    defaults: new { id = RouteParameter.Optional }
);

//调用时的url请求格式:api/products

config.Routes.MapHttpRoute( 
    name:"myApi",
    routeTemplate: "api/{controller}/{action}/{id}", //定义了api的action占位符
    defaults: new { id = RouteParameter.Optional }
);

//调用时的url请求格式:api/products/insertProduct

 

自定义客户端的消息处理器

调用API的客户端可以自定义消息处理器,消息处理器从System.Net.Http.DelegatingHandler派生,然后重写基类的SendAsync和Dispose方法,因为请求发出时会进入SendAsync方法,而服务端的响应同样也会进入SendAsync方法,所以该方法可以处理即将发送到远程API的请求,也可以处理远程API返回的数据。

using System.Net.Http;
using Newtonsoft.Json;
using System.Diagnostics;

namespace ClientCallWebAPI
{
    public class MyMessageHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, System.Threading.CancellationToken cancellationToken )
        {
            //发送Http请求,如果没有得到服务端API的响应,则输出一个日志记录,最后将HttpResponseMessage(响应消息)返回以便传递给其它的Http消息处理器
            HttpResponseMessage response = await base.SendAsync( request, cancellationToken );
            if (!response.IsSuccessStatusCode)
            {
                Trace.Listeners.Add( new TextWriterTraceListener( "f:/log.txt" ) );
                Trace.AutoFlush = true;
                Trace.WriteLine( $"响应错误:请求时间 { DateTime.Now.ToString( ) }   错误码:{ response.StatusCode }   错误详细信息:{ response.ReasonPhrase }" );
            }
            return response;
        }

protected override void Dispose( bool disposing )
        {
            base.Dispose( disposing );
        }
    }

class Program
    {
        static void Main( string[] args )
        {

            //创建HttpClient实例时需要使用HttpClientFactory的工厂方法,以便将自定义的消息处理器注册到处理管线中,每个处理器以逗号隔开即可
            //自定义的处理器总是先进后出,Create方法参数的最后一个处理器会是第一个执行处理器
            HttpClient request = HttpClientFactory.Create( new MyMessageHandler( ) ); 
            //……
        }
    }
}

自定义服务端的消息处理器

与自定义客户端的消息处理器是一样的,以下实现当请求进入服务端后,MyMessageHandler 将处理请求,将客户端IP写入日志,最后调用基类的消息处理器正常处理请求。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Net.Http;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace API.App_Start
{
    public class MyMessageHandler : DelegatingHandler
    {
        protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken )
        {
            //自定义处理请请求:请求到达后将客户端IP写入日志
            Trace.Listeners.Add( new TextWriterTraceListener( @"F:\log.txt" ) );
            Trace.AutoFlush = true;
            Trace.WriteLine( $"{DateTime.Now.ToString( "yyyy年-MM月-dd日" )} 请求到达 IP:{ IP.GetIP( )}" );
            // 调用内置的消息处理器处理请求
            var response = await base.SendAsync( request, cancellationToken );
            //向客户端输出响应:得到响应的输出对象后可以自定义一个响应头并添加到输出流返回给客户端
            response.Headers.Add( "myMessageHeader", "你好,你的请求已经成功处理" );
            return response;
        }

protected override void Dispose( bool disposing )
        {
            base.Dispose( disposing );
        }
    }

public class IP
    {
        /// <summary>
        /// 获取客户端IP地址
        /// </summary>
        /// <returns>若失败则返回回送地址</returns>
        public static string GetIP( )
        {
            //如果客户端使用了代理服务器,则利用HTTP_X_FORWARDED_FOR找到客户端IP地址
            var var = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            string userHostAddress = var != null ? var.ToString( ).Split( ',' )[].Trim( ) : "";
            //否则直接读取REMOTE_ADDR获取客户端IP地址
            if (string.IsNullOrEmpty( userHostAddress ))
            {
                userHostAddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
            }
            //前两者均失败,则利用Request.UserHostAddress属性获取IP地址,但此时无法确定该IP是客户端IP还是代理IP
            if (string.IsNullOrEmpty( userHostAddress ))
            {
                userHostAddress = HttpContext.Current.Request.UserHostAddress;
            }
            //最后判断获取是否成功,并检查IP地址的格式(检查其格式非常重要)
            if (!string.IsNullOrEmpty( userHostAddress ) && IsIP( userHostAddress ))
            {
                return userHostAddress;
            }
            return "本机IP";
        }

/// <summary>
        /// 检查IP地址格式
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        public static bool IsIP( string ip )
        {
            return System.Text.RegularExpressions.Regex.IsMatch( ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$" );
        }
    }
}

将消息处理器插入处理管道,需要在App_Start的WebApiConfig.cs中注册

public static class WebApiConfig
{
    public static void Register( HttpConfiguration config )
    {
        config.MessageHandlers.Add( new MyMessageHandler( ) );
    }
}

注册单路由消息处理器

namespace API
{
    public static class WebApiConfig
    {
        public static void Register( HttpConfiguration config )
        {
            // Web API 路由
            config.MapHttpAttributeRoutes( );

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

config.MessageHandlers.Add( new MyMessageHandler1( ) ); //全局消息处理器

config.Routes.MapHttpRoute(
                name: "SpecialApi",
                routeTemplate: "specialApi/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional },
                constraints: null, //必须提供约束,哪怕是null,否则会提示参数个数不完整,没有采用4个参数重载
                handler:new MyMessageHandler2( ) //注册特定路由的消息处理器,只针对此路由使用此消息处理器
            );
        }
    }
}

创建API帮助文档

右击API项目属性 - 生成 - 勾选生成xml文档,为xml文档命名,这会为项目生成一个xml格式的说明性文档

打开项目目录,Areas - HelpPage - App_Start - HelpPageConfig.cs,在Register方法中注册xml说明文档

public static void Register( HttpConfiguration config )
{
    config.SetDocumentationProvider( new XmlDocumentationProvider( HttpContext.Current.Server.MapPath( "~/App_Data/APIHelp.xml" ) ) );
}

在你的Api控制器中为每一个Action操作添加注释,帮助文档页面会自动显示这些注释信息。

/// <summary>
/// 根据id查询产品
/// </summary>
/// <param name="id">提供产品ID</param>
/// <returns></returns>
public HttpResponseMessage GetProduct( int id )
{
    Product item = productRepository.Get( id );
    return item == null ? throw new HttpResponseException( HttpStatusCode.NotFound ) : Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( item ) );
}

打开以下项目目录的文件可以修改帮助文档页面及其详细页面的部分英文说明为中文:

Areas - HelpPage - Views - Help - Index.cshtml,可修改帮助文档页面顶部的文字描述。

Areas - HelpPage - Views - Help -  Api.cshtml,可修改帮助文档详细页面顶部的回到帮助主页的超链接。

Areas - HelpPage - Views - Help - DisplayTemplates - ApiGroup.cshtml,可修改帮助文档页面的列头为中文。

Areas - HelpPage - Views - Help - DisplayTemplates - HelpPageApiModel.cshtml,可修改帮助文档API页面的列头为中文。

Areas - HelpPage - Views - Help - DisplayTemplates - Parameters.cshtml,可修改帮助文档API详细页面的列头为中文。

异常处理

为了便于开发人员定位http错误,也为了向客户端显示更加友好的http错误信息,你可以手动定义http异常信息,这样可以把友好的异常信息响应给客户端,也可以定义一个http异常过滤器,异常过滤器应从ExceptionFilterAttribute派生。

手动定义http异常

手动定义http异常,使用这种方式必须注意,WEB API的Http异常机制是最早开始执行的,像下面这种由开发人员编写的异常抛出逻辑是后来才执行的,也即,如果一个明显的Http异常被WEB API的异常机制捕获,比如发起请求的客户端并未提供category参数,那么以下代码的测试逻辑根本不会执行,因为未提供category参数的异常已经在手动测试的代码执行前被执行,如果没有明显的异常被WEB API捕获,则以下测试category参数的值是否在两个值的范围之内的代码逻辑才会得到执行。

public HttpResponseMessage GetProductCategory( string category )
{
    HttpResponseMessage httpResponseMessage = null;
    if (!category.Contains( "图书" ) || !category.Contains( "音乐" ))
    {
        httpResponseMessage = Request.CreateResponse( HttpStatusCode.NotFound, JsonConvert.SerializeObject( new { msg = "提供的参数值不在可查询范围之内" } ) );
        //CreateResponse方法会自动将参数2提供的value序列化为json格式:message:value
        //httpResponseMessage = Request.CreateResponse( HttpStatusCode.NotFound, "提供的参数值不在可查询范围之内" );
    }
    else
    {
        var list = productRepository.GetAll( ).Where( p => p.Category == category );
        httpResponseMessage = Request.CreateResponse( HttpStatusCode.OK, JsonConvert.SerializeObject( list ) );
    }
    return httpResponseMessage;
}

定义全局异常过滤器

你可以直接将异常过滤器应用在api控制器或api控制器的action方法上,但是,如果你创建API项目时勾选了MVC,那么过滤器先必须注册在WebApiConfig.cs中,否则无效。单经过我的测试,自定义的Http异常过滤器无效,下断点不会进入,原因不明。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

using System.Web.Http;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
using System.Diagnostics;
using Newtonsoft.Json;

namespace API.App_Start
{
    /// <summary>
    /// Http错误过滤器
    /// </summary>
    public class HttpErrorFilterAttribute: ExceptionFilterAttribute
    {
        /// <summary>
        /// 过滤Http错误
        /// </summary>
        /// <param name="context"></param>
        public override void OnException( HttpActionExecutedContext context )
        {
            var ex = context.Exception;

//将异常写入日志记录
            Trace.Listeners.Add( new TextWriterTraceListener( "f:/logforHttpError.txt" ) );
            Trace.AutoFlush = true;
            Trace.WriteLine( $"异常发生时间:{DateTime.Now.ToString( "yyyy-MM-dd HH:mm:ss" )}  " +
                $"异常类型:{context.Exception.GetType( ).ToString( )}"+
                $"堆栈信息:{context.Exception.Message}{context.Exception.StackTrace}"
                );

//无实现
            if (ex is NotImplementedException)
            {
                object jsonMsg = new { errorType = ex.GetType( ).ToString( ), message = ex.Message, statusCode = (int)HttpStatusCode.NotImplemented };
                string jsonMsgStr = JsonConvert.SerializeObject( jsonMsg );
                context.Exception= new HttpResponseException( new HttpResponseMessage( HttpStatusCode.NotImplemented )
                {
                    Content = new StringContent( jsonMsgStr ),
                    ReasonPhrase = "请求无实现"
                } );

}

//超时
            else if (ex is TimeoutException)
            {
                object jsonMsg = new { errorType = ex.GetType( ).ToString( ), message = ex.Message, statusCode = (int)HttpStatusCode.RequestTimeout };
                string jsonMsgStr = JsonConvert.SerializeObject( jsonMsg );
                context.Exception = new HttpResponseException( new HttpResponseMessage( HttpStatusCode.RequestTimeout )
                {
                    Content = new StringContent( jsonMsgStr ),
                    ReasonPhrase = "请求超时"
                } );
            }

//服务器内部错误
            else
            {
                object jsonMsg = new { errorType = ex.GetType( ).ToString( ), message = ex.Message, statusCode = (int)HttpStatusCode.InternalServerError };
                string jsonMsgStr = JsonConvert.SerializeObject( jsonMsg );
                context.Exception = new HttpResponseException( new HttpResponseMessage( HttpStatusCode.InternalServerError )
                {
                    Content = new StringContent( jsonMsgStr ),
                    ReasonPhrase = "内部服务器错误"
                } );
            }
            base.OnException( context );
        }
    }
}

namespace API
{
    public static class WebApiConfig
    {
        public static void Register( HttpConfiguration config )
        {
            config.Filters.Add( new API.App_Start.HttpErrorFilterAttribute( ) );
        }
    }
}

  

Javascript调用API

<script type="text/javascript">
    $(document).ready(function () {
    //查询数据
    $.ajax({
        url: "http://localhost:58594/api/employeemsg/Get",
        type: "get",
        success: (data) => {
            var json = $.parseJSON(data);
            $(json).each((index, item) =>alert(item.ID + item.Name));         }
  });

//添加新数据
    $.ajax({
        url: "http://localhost:58594/api/employeemsg/Add",
        data:{ ID:,Name:"lily",Gender:"男",Birthday:"2016-11-12",Age: },
        type: 'post',
        success: (data) => {
            //console.log(data);
            alert(data);
        }
    });
});
</script>

服务端调用API得到数据存入ViewBag后,可以通过如下方式将数据取出来放进Js代码中:

$(document).ready(function () {
    var jsonstr=@Html.Raw(ViewBag.msg);
    var json = $.parseJSON(jsonstr);
    $(json).each(function(index,item){
        alert(item.ID+item.Name+item.Gender+item.Birthday);
    });
});

附:下载远程数据

string url = "http://www.weather.com.cn/weathern/101040100.shtml";
WebClient wc = new WebClient();
wc.Encoding = Encoding.GetEncoding("utf-8");
string content = wc.DownloadString(url);

ASP.NET - 学习总目录

MVC - 模型验证的更多相关文章

  1. ASP.NET MVC - 模型验证

    ASP.NET MVC - 模型验证(Model verification) 模型验证原理浅析 模型验证用到了模型绑定器.模型验证器(System.Web.Mvc.DataAnnotationsMod ...

  2. asp.net mvc 模型验证组件——FluentValidation

    asp.net mvc 模型验证组件——FluentValidation 示例 using FluentValidation; public class CustomerValidator: Abst ...

  3. asp.net mvc 模型验证注解,表单提交

    一.添加模型 public class Account { public int ID { get; set; } [Display(Name = "姓名")] //设置要显示的字 ...

  4. ASP.NET没有魔法——ASP.NET MVC 模型验证

    在前面的文章中介绍了用户的注册及登录功能,在注册用户时可以通过代码的形式限制用户名及密码的格式,如果不符合要求那么就无法完成操作,如下图: 该功能的原理是Identity基于的Entity Frame ...

  5. 双重保险——前端bootstrapValidator验证+后台MVC模型验证

    我们在前端使用BoostrapValidator插件验证最基本的格式要求问题,同时在后台中,使用MVC特有的模型验证来做双重保险.对于boostrapValidator我就不说了,具体请看<bo ...

  6. asp.net mvc 模型验证-最舒服的验证方式

    在院子里发现 http://www.cnblogs.com/yangecnu/p/3759784.html 模型验证方法 1. 一般方法 繁琐, 无数的if else, 在炎炎夏天,我见过一个验证方法 ...

  7. 当ASP.NET MVC模型验证遇上CKEditor

    项目需要,使用到了CKEditor编辑器.这是个很不错的富文本编辑器,但是当它绑定的字段需要进行模型验证的时候,却会出现验证失效的问题.因此本文旨在记录这个问题和给出解决办法.以下以Validatio ...

  8. Asp.Net MVC 模型验证详解-实现客户端、服务端双重验证

    概要 在asp.net webform开发中经常会对用户提交输入的信息进行校验,一般为了安全起见大家都会在客户端进行Javascript(利于交互).服务端双重校验(安全).书写校验代码是一个繁琐的过 ...

  9. mvc模型验证

    自定义的ValidationAttribute类, Compare   [Compare("MyOtherProperty")] 两个属性必须相同值,比如我们要求用户重复输入两次邮 ...

随机推荐

  1. QString内部仍采用UTF-16存储数据且不会改变(一共10种不同情况下的编码)

    出处:https://blog.qt.io/cn/2012/05/16/source-code-must-be-utf-8-and-qstring-wants-it/ 但是注意,这只是QT运行(Run ...

  2. 135. Candy

    题目: There are N children standing in a line. Each child is assigned a rating value. You are giving c ...

  3. C#中的泛型详解

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

  4. Java Memory Management(1)

    Java Memory Management, with its built-in garbage collection, is one of the language’s finest achiev ...

  5. POJ 1840 Eps 解题报告(哈希)

    a1x13+ a2x23+ a3x33+ a4x43+ a5x53=0,xi∈[-50,50],且xi!=0.让我们求所有解的可能. 首先,如果暴力判断的话,每个x的取值有100种可能,100^5肯定 ...

  6. sharepoint2010网站根据权限隐藏ribbon

    转:http://www.it165.net/design/html/201302/1734.html 项目要求让普通用户看不到"网站操作",为了解决该问题,我找了好几篇博客,但都 ...

  7. Codeforces 14D

    #include<iostream> #include<cstdio> #include<cstring> using namespace std; const i ...

  8. localtime和localtime_r

    上程序: #include <cstdlib> #include <iostream> #include <time.h> #include <stdio.h ...

  9. 【Java基础】基本类型的包装类作为参数传递是值传递还是引用传递

    突然想到这个问题,然后做了下实验,下面以Integer来讲解,其他的忽略: import java.util.Iterator; /** * Created by lili on 15/9/24. * ...

  10. (android 源码下开发应用程序) 如何在 Android 各 level ( 包含 user space 與 kernel space ) 使用dump call stack的方法

    http://janbarry0914.blogspot.com/2014/07/androiddump-call-stack.html dump call stack [文章重點] 了解 Andro ...