web api 記錄請求

前段時間在開發一個協議站點供客戶端(Android/IOS)使用,因業務須要統計各協議的調用頻率。將記錄以日誌的形式記錄在日誌系統中。 json

  簡單分析了一下,技術方案大體分爲兩種: api

  方案A:每一個業務模塊須要埋點的協議單獨埋點。 app

  方案B:封裝一個HttpModule。記錄全部的請求。 ide

  方案A與方案B的優缺點就不在分析了。在咱們的項目有兩個個小組作相似的協議站點,咱們採用的是方案B。而另外的一個小組採用方案A。 url

  在此我重點說一下采用的方案B的理由: spa

    一、麻煩一我的,一我的開發完畢後,其餘人再也不去花時間增長記錄。統一記錄。 日誌

    二、方便排錯,還能夠作請求參數與輸出參數的記錄,也不用在業務中記錄返回的值。業務開發只關心業務便可。記錄業務中的常規日誌便可。 code

  代碼比較簡單。與協議之間走的是Post請求發送Json串。請求串全在Request.InputStream中。兩個文件一個處理請求(Handle.cs),一個獲取數據(CatchTextStream.cs) blog

Handle.cs ip

using System;
using System.IO;
using System.Web;
using Logs;

namespace HttpModule.Api
{
    public class Handle : IHttpModule
    {
        private string json = string.Empty;
        private string requestItemKey = "request.json";
        private string responseItemKey = "response.json";
        void IHttpModule.Dispose()
        {
        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(beginRequest);
            context.EndRequest += new EventHandler(endRequest);
        }
        private void beginRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            var context = application.Context;
            if (context == null)
            {
                return;
            }

            //排除不須要的連接
            if (context.Request.InputStream.Length > 0)
            {
                context.Response.Filter = new CatchTextStream(context.Response.Filter, responseItemKey);
                context.Request.InputStream.Position = 0; //設置流的位置
                StreamReader reader = new StreamReader(context.Request.InputStream); //request請求流
                string json = reader.ReadToEnd();
                context.Request.InputStream.Seek(0, SeekOrigin.End);//重置流讀取位置,若是不重置在方法中沒法獲取json串
                context.Request.RequestContext.HttpContext.Items[requestItemKey] = json;
            }
            else//只記錄請求參數
            {
                _log.Info("請求url:" + context.Request.Url);
            }

        }
        private void endRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            var context = application.Context;
            if (context.Request.RequestContext.HttpContext.Items.Contains(requestItemKey))
            {
                string requestJson = context.Request.RequestContext.HttpContext.Items[requestItemKey].ToString();
                string responseJson = context.Request.RequestContext.HttpContext.Items[responseItemKey].ToString();
                //當前請求狀態 返回值中帶code:0表示協議請求成功 status=日誌系統使用標識,1=請求正常返回,0=請求異常
                int status = responseJson.Contains("\"code\":\"0\"") ? 1 : 0;
                writeLog(context.Request.Url.LocalPath, requestJson, "return=" + responseJson, status);

            }

        }

        private void writeLog(string url, string parms, string msg, int status)
        {
            var entity = new Logs.LogRecord("站點名稱", LogLevelType.Monitor, url)
            {
                ParasNameValue = parms,
                ErrMessage = msg,
                Status = status
            };
            Logs.LogNetHelper.WriteLog(entity);
        }
    }
}

注:Logs包爲公司項目封裝的通用記錄方法。

CatchTextStream.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace HttpModule.Api
{
    class CatchTextStream : Stream
    {
        private Stream output;
        private List<byte> _listByte = new List<byte>();
        private string _itemKey;
        public CatchTextStream(Stream s, string itemKey)
        {
            _itemKey = itemKey;
            output = s;
        }
        public override bool CanRead
        {
            get { return output.CanRead; }
        }


        public override bool CanSeek
        {
            get { return output.CanSeek; }
        }


        public override bool CanWrite
        {
            get { return output.CanWrite; }
        }


        public override void Flush()
        {

            output.Flush();
            if (_listByte.Any())
            {
                var utf8 = Encoding.UTF8;
                string json = utf8.GetString(_listByte.ToArray(), 0, _listByte.Count);
                HttpContext.Current.Request.RequestContext.HttpContext.Items[_itemKey] = json;
            }
        }


        public override long Length
        {
            get { return output.Length; }
        }


        public override long Position
        {
            get { return output.Position; }
            set { output.Position = value; }
        }


        public override int Read(byte[] buffer, int offset, int count)
        {
            return output.Read(buffer, offset, count);
        }


        public override long Seek(long offset, SeekOrigin origin)
        {
            return output.Seek(offset, origin);
        }


        public override void SetLength(long value)
        {
            output.SetLength(value);
        }


        public override void Write(byte[] buffer, int offset, int count)
        {
            output.Write(buffer, offset, count);
            if (HttpContext.Current != null)
            {
                if (HttpContext.Current.Request.RequestContext.HttpContext.Items.Contains(_itemKey))
                {
                    var temp = buffer.Skip(offset).Take(count);
                    _listByte.AddRange(temp);
                }
            }
        }
    }
}

接下來在Web.config配置Modules便可。

<modules runAllManagedModulesForAllRequests="true">
      <!--記錄請求日誌-->
      <add name="apilog" type="HttpModule.Api.Handle,HttpModule.Api" />
 </modules>
相關文章
相關標籤/搜索