Google Code Prettify

顯示具有 Web API 標籤的文章。 顯示所有文章
顯示具有 Web API 標籤的文章。 顯示所有文章

web api 跨域请求,ajax跨域调用webapi

1、跨域问题仅仅发生在Javascript发起AJAX调用,或者Silverlight发起服务调用时,其根本原因是因为浏览器对于这两种请求,所给予的权限是较低的,通常只允许调用本域中的资源,除非目标服务器明确地告知它允许跨域调用。假设我们页面或者应用已在 http://www.test1.com 上了,而我们打算从 http://www.test2.com 请求提取数据。一般情况下,如果我们直接使用 AJAX 来请求将会失败,浏览器也会返回“源不匹配”的错误,"跨域"也就以此由来。
2、什么是跨域呢?
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。通常来说,跨域分为以下几类:


在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
 
3、当发起AJAX跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况。
出现如下错误信息

Failed to load https://localhost:44348/api/Employee/List: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:44302' is therefore not allowed access.


在这里跟大家解释一下 Access-Control-Allow-Origin是HTML5中定义的一种服务器端返回Response header,用来解决资源(比如字体)的跨域权限问题。
当Access-Control-Allow-Origin后面跟URL 或 *,如果是 URL 则只会允许来自该 URL 的请求,* 则允许任何域的请求
例如:header('Access-Control-Allow-Origin:http://A.abc.com')||header('Access-Control-Allow-Origin:*')
意思是说只有当你请求的资源被允许跨域的时候才可以被访问。
那么我们该怎么设置Access-Control-Allow-Origin呢?
 
4、第一种是用JSONP来获得跨域的数据,而WebAPI本身是不支持javascript的callback的
 比较一下json与jsonp格式的区别:
json格式:
{
    "message":"获取成功",
    "state":"1",
    "result":{"name":"工作组1","id":1,"description":"11"}
}
jsonp格式:
callback({
    "message":"获取成功",
    "state":"1",
    "result":{"name":"工作组1","id":1,"description":"11"}
})
看出来区别了吧,在url中callback传到后台的参数是神马callback就是神马,jsonp比json外面有多了一层,callback()。
只需要给全局注册一个JsonCallbackAttribute,就可以判断接口的访问是属于跨域,还是非跨域,正常的返回。
因为我们的接口,可能是用来给 移动端(Android 、IOS)做数据接口,也有可能是给网站用,所以,考虑到可能存在跨域的问题。
   GlobalConfiguration.Configuration.Filters.Add(new JsonCallbackAttribute());
复制代码
复制代码
 public class JsonCallbackAttribute : ActionFilterAttribute
    {
        private const string CallbackQueryParameter = "callback";

        public override void OnActionExecuted(HttpActionExecutedContext context)
        {
            var callback = string.Empty;

            if (IsJsonp(out callback))
            {
                var jsonBuilder = new StringBuilder(callback);

                jsonBuilder.AppendFormat("({0})", context.Response.Content.ReadAsStringAsync().Result);

                context.Response.Content = new StringContent(jsonBuilder.ToString());
                //context.Response.Content = new StringContent("C(\"a\")");
            }

            base.OnActionExecuted(context);
        }

        private bool IsJsonp(out string callback)
        {
            callback = System.Web.HttpContext.Current.Request.QueryString[CallbackQueryParameter];

            return !string.IsNullOrEmpty(callback);
        }
复制代码
复制代码
结合下面图片不难开出,请求的地址带回了,callback的参数标识。
当然也可以用解决跨域问题的jQuery插件-jquery-jsonp,有第一种方式的基础,使用jsonp插件也就比较简单了,server端代码无需任何改动。
5、服务端直接修改配置文件,个人认为这种方式好一点,毕竟我们所写的api是对外公开的,安全访问的控制还是要通过其他方法来保证。
针对ASP.NET MVC,只需要在web.config中添加如下的内容即可
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<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>
  
针对ASP.NET Web API,除了上面这样的设置,还需要添加一个特殊的设计,就是为每个APIController添加一个OPTIONS的方法,但无需返回任何东西。
public string Options()
{
return null; // HTTP 200 response with empty body
}
6、还有用CORS(跨域资源共享,Cross-Origin Resource Sharing)来解决的, CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。
hander() 设置,“*”号表示允许任何域向我们的服务端提交请求

     


  也可以设置指定的域名,如域名 http://www.test2.com ,那么就允许来自这个域名的请求
     


这种没有仔细研究,其实跟上面的配置文件,差不多,都是去设置响应头。
 
本文是结合好多其他作者的文章:
http://www.cnblogs.com/chenxizhang/p/3821703.html
http://www.cnblogs.com/sunxucool/p/3433992.html
http://www.cnblogs.com/Darren_code/p/cors.html






from: https://www.cnblogs.com/inconceivable/p/5504732.html

[ASP.NET]如何為 Web API 2 加上及使用 Refresh Token 功能

預設的 Web API 專案中並沒有提供 Refresh Token 的範例,現在我們來作一個。



新增 SimpleRefreshTokenProvider 實作 IAuthenticationTokenProvider

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
 //實際放到DB去存起來
 private static ConcurrentDictionary _refreshTokens = 
  new ConcurrentDictionary();

 public async Task CreateAsync(AuthenticationTokenCreateContext context)
 {
  var guid = Guid.NewGuid().ToString();

  //copy properties and set the desired lifetime of refresh token
  var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
  {
   IssuedUtc = context.Ticket.Properties.IssuedUtc,
   //時間是3個月
   ExpiresUtc = DateTime.UtcNow.AddMonths(3) 
  };
    
  var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

  _refreshTokens.TryAdd(guid, refreshTokenTicket);
  context.SetToken(guid);
 }

 public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
 {
  AuthenticationTicket ticket;

  if (_refreshTokens.TryRemove(context.Token, out ticket))
  {
   context.SetTicket(ticket);
  }
 }

 public void Create(AuthenticationTokenCreateContext context)
 {
  throw new NotImplementedException();
 }

 public void Receive(AuthenticationTokenReceiveContext context)
 {
  throw new NotImplementedException();
 }
}




然後在 App_Start\Startup.Auth.cs 中設定 OAuthOptions 裡的RefreshTokenProvider,如下,

App_Start\Startup.Auth.cs

OAuthOptions = new OAuthAuthorizationServerOptions
{
 TokenEndpointPath = new PathString("/Token"),
 Provider = new ApplicationOAuthProvider(PublicClientId),
 RefreshTokenProvider = new SimpleRefreshTokenProvider(),
 AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
 AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
 AllowInsecureHttp = true
};




當我們取回 Token 時會同取得 refresh_token 的內容




再用 Refresh Token 來取得新的 Access Token 及 Refresh Token,如下







用過的 Refresh Token 會從 ConcurrentDictionary 移除。所以;之前用過的 Token 也就失效了。



[ASP.NET]Web API 2 Unit Test 注意事項

如果你要為您的 Web API 進行單元測試,那你就必須要手動安裝 EntityFramework 才行,不然測試的結果會回覆你 "沒有這個測試的結果"。


Install-Package EntityFramework -projectname <Test project name>

[Web API]從零開始建立基於 Token 的 ASP.NET Web API 2 身份驗證機制 (Visual Studio 2017)

經由這個範例為基礎,您可以建立一套自己的 ASP.NET Web API 2 身份驗證機制,本範例是以 Visual Studio 2017 製作且在本文的後半部有詳細的資料測試畫面,但畢竟只是個範例,完整性會較不足,如密碼加密處理就不在本文中介紹。


1. 建立專案 ( TokenAuthorizationExample.csproj)

    新增專案時選擇 ASP.NET Web 應用程式


    選擇 Web API


    專案建立後系統會生成下圖的內容




2.啟用 SSL

點選 [方案總管] 中的專案名稱 (TokenAuthorizationExample),然後在屬性視窗中啟用 SSL。




點選 [方案總管] 中的專案名稱 (TokenAuthorizationExample)上點擊滑鼠右鍵,然後選擇內容,在 Web 頁籤將 URL 設定為屬性中 SSL URL 屬性中的值 ,並點選 [建立虛擬目錄]。




3.加套件


在套件管理器主控台執行以下指令藉以安裝必須的套件:

  Install-Package Microsoft.Owin.Host.SystemWeb -projectname TokenAuthorizationExample
  Install-Package Microsoft.Owin.Security.OAuth -projectname TokenAuthorizationExample
  Install-Package Microsoft.Owin.Cors -projectname TokenAuthorizationExample
  Install-Package Newtonsoft.Json -projectname TokenAuthorizationExample
  Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.2.2 -projectname TokenAuthorizationExample


4.在 App_start 中 WebApiConfig.cs 檔增加 2 行程式

預設以 Json 傳回

\App_Start\WebApiConfig.cs
using System.Linq;
using System.Web.Http;

namespace TokenAuthorizationExample
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 設定和服務

            // Web API 路由
            config.MapHttpAttributeRoutes();

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

            var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
            config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
        }
    }
}




5.加 Class

5.1
\AuthorizationServerProvider.cs
using System.Security.Claims;
using System.Threading.Tasks;

using Microsoft.Owin.Security.OAuth;

namespace TokenAuthorizationExample
{
    public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var identity = new ClaimsIdentity(context.Options.AuthenticationType); // 取得身份聲明資料
            if(context.UserName == "admin" && context.Password == "admin") {
                identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
                identity.AddClaim(new Claim("username", "admin"));
                identity.AddClaim(new Claim(ClaimTypes.Name, "morseAdmin"));
                context.Validated(identity);
            }
            else if(context.UserName == "user" && context.Password == "user") {
                identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
                identity.AddClaim(new Claim("username", "user"));
                identity.AddClaim(new Claim(ClaimTypes.Name, "morseUser"));
                context.Validated(identity);
            }
            else {
                context.SetError("Invalid_grant", "Provided username and password is incorrect.");
                return;
            }
        }
    }
}


5.2
\Startup.cs
using System;
using System.Web.Http;

using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Security.OAuth;

using Owin;

[assembly: OwinStartup(typeof(TokenAuthorizationExample.Startup))]

namespace TokenAuthorizationExample
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 如需如何設定應用程式的詳細資訊,請瀏覽 https://go.microsoft.com/fwlink/?LinkID=316888
            // enable cors origin requests
            app.UseCors(CorsOptions.AllowAll);

            var myProvider = new AuthorizationServerProvider();
            OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = myProvider
            };
            app.UseOAuthAuthorizationServer(options);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
        }
    }
}


5.3
\AuthorizeAttribute.cs
using System.Web;
using System.Web.Http.Controllers;

namespace TokenAuthorizationExample
{
    public class AuthorizeAttribute : System.Web.Http.AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
        {
            if(!HttpContext.Current.User.Identity.IsAuthenticated)
                base.HandleUnauthorizedRequest(actionContext);
            else
                actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
        }
    }
}




6.加 Controller

\Controllers\DataController.cs
using System;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;

namespace TokenAuthorizationExample.Controllers
{
    public class DataController : ApiController
    {
        [AllowAnonymous]
        [HttpGet]
        [Route("api/data/getall")]
        public IHttpActionResult Get()
        {
            return Ok("Now server time is: " + DateTime.Now.ToString());
        }
        [Authorize]
        [HttpGet]
        [Route("api/data/hello")]
        public IHttpActionResult GetHello()
        {
            var identity = (ClaimsIdentity)User.Identity;
            return Ok("Hellow " + identity.Name);
        }
        [Authorize(Roles = "admin")]
        [HttpGet]
        [Route("api/data/GetForAdmin")]
        public IHttpActionResult GetForAdmin()
        {
            var identity = (ClaimsIdentity)User.Identity;
            var role = identity.Claims
                .Where(c => c.Type == ClaimTypes.Role)
                .Select(c => c.Value);
            return Ok("Hellow " + identity.Name + ", Role: " + string.Join(",", role.ToList()));
        }
    }
}


7.測試

AllowAnonymous (不控管權限) 可以正常取得資料



取得 admin 令牌 (token)


以 admin 令牌 (token) 取得以 Authorize 控管權限的資料


以 admin 令牌 (token) 取得以 Authorize(Roles = "admin") 控管權限的資料





取得 user 令牌 (token)



METH: POSTHEADERS:             Content-Type: application/x-www-form-urlencodedBODY:             username: 你的帳號             password: 你的密碼             grant_type: password




 以 user 令牌 (token) 取得以 Authorize 控管權限的資料




METH: GETHEADERS:             Authorizationbearer [token access code]





 以 user 令牌 (token) 取得以 Authorize(Roles = "admin") 控管權限的資料時傳回 Http 403 。






[Web API]實用的開發範例 - 前端資料存取

這個範例我在撰寫時是有分為 Web API (REST web service) 及用戶端二大模塊,現在有很多的工程在前端 (用戶端) 的實作都是以 Javascript 這類的前端工程工具來製作,這些前端工具是有它的好處,至少在視覺上就能提高很多,但是;將它拿來向後端 (資料庫或Web服務) 拿資料,總覺得不太安全,由其是客戶的經營及生產方面的工程。


聲明:
1. 本範例屬中高階工程技術範疇,不適合初學者。
2. 學習本範例需要對 Web API 有基礎的概念。


以下的範例主要是示範前端 (C#) 向後端 Web API (REST web service) 拿資料,而我們會提供需權限認證及免權限認證二類的資料來源,而權限認證我們是以令牌 (Token) 的方式進行控管的。


修改 Web API 的控制器 (ValuesController.cs) :

\Controllers\ValuesController.cs
using System;
using System.Web.Http;

namespace LocalAccountsApp.Controllers
{
    [Authorize]
    public class ValuesController : ApiController
    {
        // GET api/values
        [AllowAnonymous]
        [HttpGet]
        [Route("api/GetValues")]
        public string GetValues()
        {
            return "嗨, 您來了.(http get)";
        }
        [HttpGet]
        [Route("api/GetAuthValues")]
        public string GetAuthValues()
        {
            var userName = this.RequestContext.Principal.Identity.Name;
            return String.Format("嗨, {0} 您來了.(http get)[by auth]", userName);
        }
        [AllowAnonymous]
        [HttpPost]
        [Route("api/PostValues")]
        public string PostValues()
        {
            return "嗨, 您來了.(http post)";
        }
        [HttpPost]
        [Route("api/PostAuthValues")]
        public string PostAuthValues()
        {
            var userName = this.RequestContext.Principal.Identity.Name;
            return String.Format("嗨, {0} 您來了.(http post)[by auth]", userName);
        }
    }
}



前端建立主控台應用程式專案,並修改 Program.cs :

\Program.cs
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;

using Newtonsoft.Json;

namespace HttpWebRequestGetTokenByPasswordExample
{
    public class MorseDataControl
    {
        MorseHttpWeb morseHttpWeb = new MorseHttpWeb();
        #region Get
        public string GetValues(string url)
        {
            return morseHttpWeb.GetHttpWeb(url);
        }
        public string GetAuthValues(string url, string accessToken)
        {
            return morseHttpWeb.GetHttpWeb(url, accessToken);
        }
        public async Task<string> GetValuesAsync(string url)
        {
            return await morseHttpWeb.GetHttpWebAsync(url);
        }
        public async Task<string> GetAuthValuesAsync(string url, string accessToken)
        {
            return await morseHttpWeb.GetHttpWebAsync(url, accessToken);
        }
        #endregion
        #region Post
        public string PostValues(string url, string query)
        {
            return morseHttpWeb.PostHttpWeb(url, query);
        }
        public async Task<string> PostValuesAsync(string url, string query)
        {
            return await morseHttpWeb.PostHttpWebAsync(url, query);
        }
        public string PostAuthValues(string url, string query, string accessToken)
        {
            return morseHttpWeb.PostHttpWeb(url, query, accessToken);
        }
        public async Task<string> PostAuthValuesAsync(string url, string query, string accessToken)
        {
            return await morseHttpWeb.PostHttpWebAsync(url, query, accessToken);
        }
        #endregion
    }
    public class MorseHttpWeb
    {
        /// <summary>
        /// 取得令牌
        /// </summary>
        /// <param name="url"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns>令牌 (Token)</returns>
        public accessToken GetAccessToken(string url, string username, string password)
        {
            accessToken ret = new accessToken();
            WebRequest request = HttpWebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            // 方法1:
            // NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
            // outgoingQueryString.Add("grant_type", "password");
            // outgoingQueryString.Add("username", username);
            // outgoingQueryString.Add("password", password);
            // byte[] postBytes = new ASCIIEncoding().GetBytes(outgoingQueryString.ToString()); // 轉成 byte[]

            //方法2:
            string s = string.Format("username={0}&password={1}&grant_type=password", username, password);
            byte[] postBytes = new ASCIIEncoding().GetBytes(s); // 轉成 byte[]

            Stream postStream = request.GetRequestStream();//取得用以寫入要求資料的 Stream 物件。
            postStream.Write(postBytes, 0, postBytes.Length);
            postStream.Flush();
            postStream.Close();

            using(WebResponse response = request.GetResponse()) {
                using(StreamReader streamReader = new StreamReader(response.GetResponseStream())) {
                    ret = JsonConvert.DeserializeObject<accessToken>(streamReader.ReadToEnd().Replace(".", ""));
                }
            }
            return ret;
        }
        /// <summary>
        /// 取得令牌(非同步)
        /// </summary>
        /// <param name="url"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns>令牌 (Token)</returns>
        public async Task<accessToken> GetAccessTokenAsync(string url, string username, string password)
        {
            accessToken ret = new accessToken();
            WebRequest request = HttpWebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            // 方法1:
            // NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
            // outgoingQueryString.Add("grant_type", "password");
            // outgoingQueryString.Add("username", username);
            // outgoingQueryString.Add("password", password);
            // byte[] postBytes = new ASCIIEncoding().GetBytes(outgoingQueryString.ToString()); // 轉成 byte[]

            //方法2:
            string s = string.Format("username={0}&password={1}&grant_type=password", username, password);
            byte[] postBytes = new ASCIIEncoding().GetBytes(s); // 轉成 byte[]

            Stream postStream = await request.GetRequestStreamAsync(); //傳回以非同步作業方式將資料寫入網際網路資源的 Stream
            postStream.Write(postBytes, 0, postBytes.Length);
            postStream.Flush();
            postStream.Close();

            using(WebResponse response = await request.GetResponseAsync()) {
                using(StreamReader streamReader = new StreamReader(response.GetResponseStream())) {
                    ret = JsonConvert.DeserializeObject<accessToken>((await streamReader.ReadToEndAsync()).Replace(".", ""));
                }
            }
            return ret;
        }
        /// <summary>
        /// HTTP GET Method Process
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="accessToken">令牌 (Token)</param>
        /// <returns></returns>
        public string GetHttpWeb(string uri, string accessToken = null)
        {
            string retVal = "";
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = "GET";
            if(accessToken != null)
                request.Headers.Add("Authorization", "Bearer " + accessToken);
            try {
                using(var response = request.GetResponse())
                    if(response != null) {
                        var reader = new StreamReader(response.GetResponseStream());
                        if(reader != null)
                            retVal = reader.ReadToEnd();
                    }
            }
            catch(Exception ex) {
                Console.WriteLine("Error: " + ex.ToString());
            }
            return retVal;
        }
        /// <summary>
        /// HTTP GET Method Process(非同步)
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="accessToken">令牌 (Token)</param>
        /// <returns></returns>
        public async Task<string> GetHttpWebAsync(string uri, string accessToken = null)
        {
            string retVal = "";
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = "GET";
            if(accessToken != null)
                request.Headers.Add("Authorization", "Bearer " + accessToken);
            try {
                using(var response = await request.GetResponseAsync())
                    if(response != null) {
                        var reader = new StreamReader(response.GetResponseStream());
                        if(reader != null)
                            retVal = await reader.ReadToEndAsync();
                    }
            }
            catch(Exception ex) {
                Console.WriteLine("Error: " + ex.ToString());
            }
            return retVal;
        }
        /// <summary>
        /// HTTP POST Method Process
        /// </summary>
        /// <param name="post_url"></param>
        /// <param name="post_query_string"></param>
        /// <returns></returns>
        public string PostHttpWeb(string post_url, string post_query_string, string accessToken = null)
        {
            var ret = "";
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(post_url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            if(accessToken != null)
                request.Headers.Add("Authorization", "Bearer " + accessToken);
            try {
                using(var writer = new StreamWriter(request.GetRequestStream())) writer.Write(post_query_string);
                using(var response = request.GetResponse()) {
                    var reader = new StreamReader(response.GetResponseStream());
                    if(reader != null)
                        ret = reader.ReadToEnd();
                }
            }
            catch(Exception ex) {
                Console.WriteLine("Error: " + ex.ToString());
            }
            return ret;
        }
        /// <summary>
        /// HTTP POST Method Process(非同步)
        /// </summary>
        /// <param name="post_url"></param>
        /// <param name="post_query_string"></param>
        /// <param name="accessToken">令牌 (Token)</param>
        /// <returns></returns>
        public async Task<string> PostHttpWebAsync(string post_url, string post_query_string, string accessToken = null)
        {
            var ret = "";
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(post_url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            if(accessToken != null)
                request.Headers.Add("Authorization", "Bearer " + accessToken);
            try {
                using(var writer = new StreamWriter(await request.GetRequestStreamAsync())) writer.Write(post_query_string);
                using(var response = await request.GetResponseAsync()) {
                    var reader = new StreamReader(response.GetResponseStream());
                    if(reader != null)
                        ret = await reader.ReadToEndAsync();
                }
            }
            catch(Exception ex) {
                Console.WriteLine("Error: " + ex.ToString());
            }
            return ret;
        }
    }
    public class MorseProcess
    {
        public MorseProcess()
        {
        }
        public async Task HttpWebTest() { 
            string userName = "xxxxxx@gmail.com";
            string userPassword = "xxxxxx";
            string urlString = "https://localhost:8087/token";

            Console.WriteLine((new MorseDataControl()).GetValues("https://localhost:8087/api/GetValues"));
            Console.WriteLine(await (new MorseDataControl()).GetValuesAsync("https://localhost:8087/api/GetValues"));
            Console.WriteLine((new MorseDataControl()).PostValues("https://localhost:8087/api/PostValues", ""));
            Console.WriteLine(await (new MorseDataControl()).PostValuesAsync("https://localhost:8087/api/PostValues", ""));

            accessToken t = (new MorseHttpWeb()).GetAccessToken(urlString, userName, userPassword); // 令牌
            accessToken tAsync = (new MorseHttpWeb()).GetAccessToken(urlString, userName, userPassword); // 令牌
            Console.WriteLine("Token 1: " + t.access_token);
            Console.WriteLine("Token 2: " + tAsync.access_token);

            Console.WriteLine((new MorseDataControl()).GetAuthValues("https://localhost:8087/api/GetAuthValues", t.access_token));
            Console.WriteLine(await (new MorseDataControl()).GetAuthValuesAsync("https://localhost:8087/api/GetAuthValues", t.access_token));
            Console.WriteLine((new MorseDataControl()).PostAuthValues("https://localhost:8087/api/PostAuthValues", "", t.access_token));
            Console.WriteLine(await (new MorseDataControl()).PostAuthValuesAsync("https://localhost:8087/api/PostAuthValues", "", t.access_token));
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            (new MorseProcess()).HttpWebTest().Wait();
            Console.Write("Press any key...");
            Console.ReadKey();
        }
    }
    public class accessToken
    {
        public dynamic access_token { get; set; }
        public string token_type { get; set; }
        public int expires_in { get; set; }
        public string userName { get; set; }
        public DateTime issued { get; set; }
        public DateTime expires { get; set; }
    }
}


執行結果:


[Web API]手動傳回 JSON 或任何格式的內容

當我們在製作 Web API 系統時,在大部份的情況下我們會需要傳回 JSON 的資料,但通常也是會有圖片或其他各類的電子檔傳回需求,碰到這種情形,我們可以將 Web API 方法的回傳型別宣告為 HttpResponseMessage。 使用 HttpResponseMessage 我們可以傳回 JSON 資料、 圖片影像、文本資料、檔案下載...等都沒問題, 參考以下範例:



/// <summary>  
/// 傳回圖片 
/// </summary> 
[HttpGet]
[Route("api/User/GetImageFile")]
public HttpResponseMessage GetImageFile()
{
 var imgPath = @"D:\00PowerData\Bird.fw.png";
 var imgByte = File.ReadAllBytes(imgPath);
 var imgStream = new MemoryStream(File.ReadAllBytes(imgPath));
 var resp = new HttpResponseMessage(HttpStatusCode.OK) {
  Content = new ByteArrayContent(imgByte)
 };
 resp.Content.Headers.ContentType = new MediaTypeHeaderValue("image/*");
 return resp;
}
/// <summary>  
/// 下載電子檔 
/// </summary> 
[HttpGet] [Route("api/User/GetFile")] public HttpResponseMessage GetFile() { var imgName = "業務填單原始資料.xls"; var imgPath = string.Format(@"C:\Temp\{0}", imgName); var imgByte = File.ReadAllBytes(imgPath); var imgStream = new MemoryStream(File.ReadAllBytes(imgPath)); var resp = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(imgByte) }; resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/force-download"); resp.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = imgName }; return resp; }
/// <summary>  
/// 傳回 json 資料  
/// </summary>  
[HttpGet]
[Route("api/Test/GetJson")]
public HttpResponseMessage GetJson()
{
 var jsonStr = "{\"IsSuccess\":true,\"Data\":\"www.itdos.com\"}";
 var result = new HttpResponseMessage(HttpStatusCode.OK) {
  Content = new StringContent(jsonStr, Encoding.UTF8, "text/json")
 };
 return result;
}
/// <summary>  
/// 傳回文字資料(含 Html 文字也行)  
/// </summary>  
[HttpGet]
[Route("api/Test/GetHtml")]
public HttpResponseMessage GetHtml()
{
 var str = "返回資料測試<b>返回資料測試</b> <a href='www.google.com'>www.google.com</a>";
 var result = new HttpResponseMessage(HttpStatusCode.OK) {
  Content = new StringContent(str, Encoding.UTF8, "text/plain")
 };
 return result;
}

HTTP content-typ 可以參考 [這裡]
HTTP status code 可以參考 [這裡]
HTTP response header information 可以參考 [這裡]

[Web API]讓 Web API 預設傳回 JSON 字串

在 \App_Start\WebApiConfig.cs 中加二行程式,加上之後,以後所有回傳的資料就都會是 JSON 字串


using System.Web.Http;
using System.Linq;

namespace TokenTest
{
    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
            config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
        }
    }
}

之後不必用 string s = JsonConvert.SerializeObject(listUserInfo) ,回傳的範例程式如下:

[HttpGet]
[Route("api/User/GetAllUserInfo")]
public List<UserInfo> GetAllAsync()
{
 List<UserInfo> listUserInfo = new List<UserInfo>();
 var db = new TokenDbContext();
 var usr = db.User.SqlQuery("select * from Users where Name='Morse'");
 foreach(User u in usr) {
  listUserInfo.Add(new UserInfo {
   Id = u.Id.ToString(),
   Name = u.Name,
   Password = u.Password
  });
 }
 return listUserInfo;
}

Rest let  執行結果:


Chrome 執行結果:

Firefox 執行結果:

[WebAPI]REST Web Service 控制器的參考範例

這個範例是用 Visual Studio 2017 生成的, 放在這備忘, 也順便提供大家參考使用。


EmployeesController.cs
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;

using WebApplication2.Models;

namespace WebApplication2.Controllers {
    public class EmployeesController : ApiController
    {
        private MvcTestDbEntities db = new MvcTestDbEntities();

        // GET: api/Employees
        /// <summary>
        /// 取回所有 Employee 的資料明細
        /// </summary>
        /// <returns>IQueryable<employee></employee></returns>
        public IQueryable<employee> GetEmployee()
        {
            return db.Employee;
        }

        // GET: api/Employees/5
        /// <summary>
        /// 取回特定 id 的 Employee 資料
        /// </summary>
        /// <param name="id" />Employee ID
        /// <returns>typeof(Employee)</returns>
        [ResponseType(typeof(Employee))]
        public async Task<ihttpactionresult> GetEmployee(string id)
        {
            Employee employee = await db.Employee.FindAsync(id);
            if (employee == null)
            {
                return NotFound();
            }

            return Ok(employee);
        }

        // PUT: api/Employees/5
        /// <summary>
        /// 變更 Employee 資料
        /// </summary>
        /// <param name="id" />Employee ID
        /// <param name="employee" />Employee
        /// <returns>typeof(void)</returns>
        [ResponseType(typeof(void))]
        public async Task<ihttpactionresult> PutEmployee(string id, Employee employee)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != employee.emp_no)
            {
                return BadRequest();
            }

            db.Entry(employee).State = EntityState.Modified;

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!EmployeeExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/Employees
        /// <summary>
        /// 新增 Employee 資料
        /// </summary>
        /// <param name="employee" />Employee
        /// <returns>typeof(Employee)</returns>
        [ResponseType(typeof(Employee))]
        public async Task<ihttpactionresult> PostEmployee(Employee employee)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Employee.Add(employee);

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateException)
            {
                if (EmployeeExists(employee.emp_no))
                {
                    return Conflict();
                }
                else
                {
                    throw;
                }
            }

            return CreatedAtRoute("DefaultApi", new { id = employee.emp_no }, employee);
        }

        // DELETE: api/Employees/5
        /// <summary>
        /// 刪除 Employee 資料
        /// </summary>
        /// <param name="id" />Employee ID
        /// <returns>typeof(Employee)</returns>
        [ResponseType(typeof(Employee))]
        public async Task<ihttpactionresult> DeleteEmployee(string id)
        {
            Employee employee = await db.Employee.FindAsync(id);
            if (employee == null)
            {
                return NotFound();
            }

            db.Employee.Remove(employee);
            await db.SaveChangesAsync();

            return Ok(employee);
        }

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

        private bool EmployeeExists(string id)
        {
            return db.Employee.Count(e => e.emp_no == id) > 0;
        }
    }
}




[WebAPI]以WebAPI的POST方法上傳檔案的參考範例


public IHttpActionResult Post(UploadFileInfo fi) {
 string dir = HttpContext.Current.Server.MapPath("~/Upload");
 using(FileStream fileStream = new FileStream(
     Path.Combine(dir, fi.FileName), 
     FileMode.OpenOrCreate, 
     FileAccess.Write)) {
  fileStream.Write(fi.FileContent, 0, fi.FileContent.Length);
 }
 return Ok(dir);
}


































[WebAPI]將 JSON 字串 POST 到 Web API

要將 JSON 編碼內容 POST 到 Web API, 首先要先準備一支方法程式 (method),用 JSON.NET 將資料轉換成 JSON 格式的字串資料,再利用 HttpClient 將它送到 Web API (遠端伺服器端) ,這一支方法程式的範例如下: ( JsonPostAsync method)


private static async Task<apiresult> JsonPostAsync(APIData apiData)
{
    APIResult fooAPIResult;
    using (HttpClientHandler handler = new HttpClientHandler())
    {
        using (HttpClient client = new HttpClient(handler))
        {
            try
            {
                #region 呼叫遠端 Web API
                string FooUrl = $"http://localhost:8089/api/Values"; 
                HttpResponseMessage response = null;

                #region  設定相關網址內容
                var fooFullUrl = $"{FooUrl}";

                // Accept 用於宣告客戶端要求服務端回應的文件型態 (底下兩種方法皆可任選其一來使用)
                //client.DefaultRequestHeaders.Accept.TryParseAdd("application/json");
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                // Content-Type 用於宣告遞送給對方的文件型態
                //client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");

                var fooJSON = JsonConvert.SerializeObject(apiData);
                // https://msdn.microsoft.com/zh-tw/library/system.net.http.stringcontent(v=vs.110).aspx
                using (var fooContent = new StringContent(fooJSON, Encoding.UTF8, "application/json"))
                {
                    response = await client.PostAsync(fooFullUrl, fooContent);
                }
                #endregion
                #endregion

                #region 處理呼叫完成 Web API 之後的回報結果
                if (response != null)
                {
                    if (response.IsSuccessStatusCode == true)
                    {
                        // 取得呼叫完成 API 後的回報內容
                        String strResult = await response.Content.ReadAsStringAsync();
                        fooAPIResult = JsonConvert.DeserializeObject<apiresult>(strResult, new JsonSerializerSettings { MetadataPropertyHandling = MetadataPropertyHandling.Ignore });
                    }
                    else
                    {
                        fooAPIResult = new APIResult
                        {
                            Success = false,
                            Message = string.Format("Error Code:{0}, Error Message:{1}", response.StatusCode, response.RequestMessage),
                            Payload = null,
                        };
                    }
                }
                else
                {
                    fooAPIResult = new APIResult
                    {
                        Success = false,
                        Message = "應用程式呼叫 API 發生異常",
                        Payload = null,
                    };
                }
                #endregion
            }
            catch (Exception ex)
            {
                fooAPIResult = new APIResult
                {
                    Success = false,
                    Message = ex.Message,
                    Payload = ex,
                };
            }
        }
    }

    return fooAPIResult;
}


接下來,我們須在控制器 (Controller) 中加上一個 Post 的方法 (method),

[HttpPost]
public APIResult Post([FromBody]APIData value)
{
    APIResult foo;

    if (value.Id == 777)
    {
        foo = new APIResult()
        {
            Success = true,
            Message = "透過 post 方法,接收到 Id=777 資料",
            Payload = value
        };
    }
    else
    {
        foo = new APIResult()
        {
            Success = false,
            Message = "無法發現到指定的 ID",
            Payload = null
        };
    }
    return foo;
}


在這個 Post 方法中,我們帶入一個 APIData 型別的物件當物件中 Id 這個屬性值為 777 的時候,在 Web API 的動作將會回覆這次的呼叫是成功的且傳回資料型別為 APIData 的 value 物件,否則,會回覆此次呼叫失敗(無法發現到指定的 ID),並傳回 null。


在 Post 的方法實作完成後,接著我們要準備程式的進入點方法(Main method)。


static void Main(string[] args)
{
    var fooAPIData = new APIData()
    {
        Id = 777,
        Name = "VulcanSource",
    };
    var foo = JsonPostAsync(fooAPIData).Result;
    Console.WriteLine($"使用 JSON 格式與使用 Post 方法呼叫 Web API 的結果");
    Console.WriteLine($"結果狀態 : {foo.Success}");
    Console.WriteLine($"結果訊息 : {foo.Message}");
    Console.WriteLine($"Payload : {foo.Payload}");
    Console.WriteLine($"");

    Console.WriteLine($"Press any key to Exist...{Environment.NewLine}");
    Console.ReadKey();

    fooAPIData = new APIData()
    {
        Id = 123,
        Name = "VulcanSource",
    };
    foo = JsonPostAsync(fooAPIData).Result;
    Console.WriteLine($"使用 JSON 格式與使用 Post 方法呼叫 Web API 的結果");
    Console.WriteLine($"結果狀態 : {foo.Success}");
    Console.WriteLine($"結果訊息 : {foo.Message}");
    Console.WriteLine($"Payload : {foo.Payload}");
    Console.WriteLine($"");

    Console.WriteLine($"Press any key to Exist...{Environment.NewLine}");
    Console.ReadKey();
}


方法中我們分為兩個部分,先是傳送  Id 這個屬性值為 777 的資料到 Web API ,接著再傳送  Id 這個屬性值為 123 的資料到 Web API ,看看這兩個部分執行後得到的結果是不是如預期。


在此,執行結果我們就不再演示,希望此文章能對你有幫助。