已回答 在...之间允许请求

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
您好,

我有一个asp.net Web API。客户希望我添加一个有效的时间间隔(09:00-21:30)以便接受请求。假设某个请求是在08:00发出的,则该消息会被拒绝,并显示一条消息,指出允许在9:00 AM和21:30 PM之间进行请求。我打算按以下方式在global.asax中实现此逻辑,但是我不确定是否有更好的方法。

C#:
ublic class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            
            GlobalConfiguration.Configure(WebApiConfig.Register);
          
        }

        protected void Application_BeginRequest()
        {
            

            if (CheckTime9To2130())
            {
                //do something
            }
            else
            {
                Response.StatusCode = 429;
                Response.Write("Requests are permitted between 9:00 AM 和 21:30 PM");
                Response.End();
            }
        }

        private bool CheckTime9To2130()
        {
            var h = DateTime.Now.TimeOfDay.Hours;
            var m = DateTime.Now.TimeOfDay.Minutes;

            if ((9 <= h && h <= 21) || (h == 21 && m <= 30))
            {
                return true;
            }

            return false;
        }
    }

如果客户想更改时间间隔怎么办?我需要在代码库中更改它并将其部署到服务器。因此,我认为我需要将这些时间用于config的设置中。但是它仍然有成本,当我在配置上更改它时,IIS将重新启动对吗?因此,将这些时间保留在数据库中似乎是唯一的解决方案?您有什么建议?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
IIS会重启吗?
关。 IIS实际上将保持运行。运行您的代码的应用程序池将重新启动。

但是,如果重新启动怎么办? IIS的默认设置是应用程序池将每29小时重新启动一次。 (如果您不想在一天当中重新启动应用程序池,则需要更改默认设置。)但是,为什么这很重要?如果在应用程序池重新启动时收到请求,IIS将保留该请求几分钟,直到应用程序池启动。

Anyway your code for that time check could be improved dramatically. Take advantage of the DateTime.TimeOfDay property, 和 the fact that you can do greater than/less than comparison with TimeSpans. Couple that with the fact that you can use TimeSpan.Parse() to read a value from your web.config, 和 you'll have more readable maintainable code.
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
好了,那么您最好使您的应用程序池启动更快。 :) 使用惰性初始化有很大帮助。在Global.asax和静态类实例中绝对不需要做的任何事情都应该延迟。

那还是使用负载均衡器。从负载均衡器中取出一台计算机,更改设置,使其预热,然后交换计算机。更新另一台计算机上的设置,然后再将其恢复。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
您也可以在单个服务器上拥有多个应用程序。不过,负载均衡器设置只会变得更加复杂。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
Lazy initialization of simply the technique of delaying creating or initializing an object until it is first requested. .NET Framework 3.5(?) introduced the Lazy<T> class, to cut down on repetitive code that looks like this:
C#:
class Foo
{
    private Bar _bar;

    public Bar Bar
    {
        get
        {
            if (_bar == null)
                _bar = new Bar();
            return _bar;
        }
    }
}
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
这就是为什么我建议从web.config获取设置而不是读取数据库的原因。当您阅读web.config时,.NET Framework对于从web.config中缓存值非常聪明,因为它知道当web.config更改时,应用程序池需要重新启动。因此,读取设置非常非常非常快。

当您从数据库中读取数据时,您需要创建一个连接,执行查询,并读取结果。您需要这样做 每一个 网络请求。这次加起来很快。是的,您可以添加一些缓存,以便您实际上每隔几分钟便会访问一次数据库,但是添加缓存的复杂性值得吗?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
就个人而言,如果我已经在使用ASP.NET WebAPI,则可以利用其其余基础结构并使用 IAuthorizationFilter。是的,可能必须用该过滤器标记每个控制器或方法,这可能需要做更多的工作,但是它还提供了灵活性,允许Web应用程序继续运行并接受您希望继续在中断窗口中运行的其他内容的Web请求。也许,如果您认为值得实施停电时间的数据库存储,那么其中之一"always on"Web API可能是一种动态设置停电时间(并刷新缓存)的方法。
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
在我的Web API中,我正在使用基本身份验证。因此,我已经实现了“授权过滤器属性”,如下所示:

C#:
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
    {

        private const string Realm = "My Realm";
        readonly Func<IUserValidate> factory;

        public BasicAuthenticationAttribute(Func<IUserValidate> factory)
        {
            this.factory = factory;
        }


        public override void OnAuthorization(HttpActionContext actionContext)
        {
            //If the Authorization header is empty or null
            //then return Unauthorized
            if (actionContext.Request.Headers.Authorization == null)
            {
                actionContext.Response = actionContext.Request
                    .CreateResponse(HttpStatusCode.Unauthorized);
                // If the request was unauthorized, add the WWW-Authenticate header
                // to the response which indicates that it require basic authentication
                if (actionContext.Response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    actionContext.Response.Headers.Add("WWW-Authenticate",
                        string.Format("Basic realm=\"{0}\"", Realm));
                }
            }
            else
            {
                //Get the authentication token from the request header
                string authenticationToken = actionContext.Request.Headers
                    .Authorization.Parameter;
                try
                {
                    //Decode the string
                    string decodedAuthenticationToken = Encoding.UTF8.GetString(
                        Convert.FromBase64String(authenticationToken));
                    //Convert the string into an string array
                    string[] usernamePasswordArray = decodedAuthenticationToken.Split(':');
                    //First element of the array is the username
                    string username = usernamePasswordArray[0];
                    //Second element of the array is the password
                    string password = usernamePasswordArray[1];
                    //call the login method to check the username 和 password
                    IUserValidate uv = factory();
                    if (uv.Login(username, password))
                    {
                        var identity = new GenericIdentity(username);
                        IPrincipal principal = new GenericPrincipal(identity, null);
                        Thread.CurrentPrincipal = principal;
                        if (HttpContext.Current != null)
                        {
                            HttpContext.Current.User = principal;
                        }
                    }
                    else
                    {
                        actionContext.Response = actionContext.Request
                            .CreateResponse(HttpStatusCode.Unauthorized);
                    }
                }
                catch
                {

                    actionContext.Response = actionContext.Request
                        .CreateResponse(HttpStatusCode.Unauthorized);

                }

            }
        }

    }

我想我应该在else部分中添加检查日期时间逻辑,并且这样吗?
C#:
if (!CheckRequestTime())
            {
                Response.StatusCode = 429;
                Response.Write("Requests are permitted between " + WebConfigurationManager.AppSettings["TimeStart"].ToString() + " AM 和 " + WebConfigurationManager.AppSettings["TimeEnd"].ToString()+ " PM.");
                Response.End();
            }
        }

        private bool CheckRequestTime()
        {
          
            var t = DateTime.Now.TimeOfDay;
            var s = TimeSpan.Parse(WebConfigurationManager.AppSettings["TimeStart"]);
            var e = TimeSpan.Parse(WebConfigurationManager.AppSettings["TimeEnd"]);

            if ((s <= t) && (t <= e))
            {
                return true;
            }


            return false;
        }
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
或者甚至在实际检查身份之前进行时间检查...

并且出于代码卫生的考虑,将其实现在另一个类中,该类也是一个授权过滤器。我记得,ASP.NET MVC允许您放置多个过滤器。如果没有,那么您还必须实现责任链模式,以使所有授权过滤器都以正确的顺序被调用。
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
工作正常,外观如何 @跳伞 , 有什么建议么?

有效时间间隔的授权过滤器:
C#:
public class ValidTimeFilter : AuthorizationFilterAttribute
    {
        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            //Check Request Start-End Period
            if (!CheckRequestTime())
            {
                var response = new HttpResponseMessage
                {
                    StatusCode = (HttpStatusCode)429,
                    ReasonPhrase = "Invalid Request Time",
                    Content = new StringContent("Requests are permitted between " + WebConfigurationManager.AppSettings["TimeStart"].ToString() + " AM 和 " + WebConfigurationManager.AppSettings["TimeEnd"].ToString() + " PM.")

                };

                actionContext.Response = response;
            }
        }

        private bool CheckRequestTime()
        {

            var t = DateTime.Now.TimeOfDay;
            var s = TimeSpan.Parse(WebConfigurationManager.AppSettings["TimeStart"]);
            var e = TimeSpan.Parse(WebConfigurationManager.AppSettings["TimeEnd"]);

            if ((s <= t) && (t <= e))
            {
                return true;
            }

            return false;
        }
    }

每日总销售额的授权过滤器:
C#:
public class ValidTotalDailySale : AuthorizationFilterAttribute
    {
        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            double totalSale = Double.Parse(WebConfigurationManager.AppSettings["TotalDailySale"].ToString());
            
            var terminal_number = HttpContext.Current.Request.Params["shopNo"];
            using (GameContext ctx = new GameContext())
            {


                
                var totalDailySales = ctx.Database.SqlQuery<Double>(
                    "WITH [CTE] AS ( SELECT [CR].[totalPrice] , [C].[ReferenceID] , ROW_NUMBER() OVER (PARTITION BY [GR].[ShopNo], [C].[ReferenceID] ORDER BY [C].[ConfirmCancelDateTime]) AS [RN] FROM [GameRequests] AS [GR] JOIN [GameConfirmResponses] AS [CR] ON [CR].referenceId = [GR].referenceId JOIN [ConfirmCancels] AS [C] ON [GR].[referenceId] = [C].[referenceId] WHERE [C].[Status] = 1 AND [C].[ShopNo] = @shopNo ) SELECT SUM([CTE].[totalPrice]) AS [TotalPrice] FROM [CTE] WHERE [CTE].[RN] = 1;",
                    new SqlParameter("shopNo", terminal_number)).ToList();

                if (totalDailySales.Count > 0 && totalDailySales[0]>totalSale)
                {
                    var response = new HttpResponseMessage
                    {
                        StatusCode = (HttpStatusCode)429,
                        ReasonPhrase = "Too Many Requests",
                        Content = new StringContent("Daily total sales limit " + totalSale + ", is exceeded for "+ terminal_number)

                    };

                    actionContext.Response = response;
                }
            
            }
        }
    }

这是我的注册方式:

C#:
//Registering ValidTime Filter
            config.Filters.Add(new ValidTimeFilter());
            //Registering ValidRequestInterval Filter
            config.Filters.Add(new ValidIntervalFilter());
            //Registering ValidTotalDailySales Filter
            config.Filters.Add(new ValidTotalDailySale());
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
这是我的个人风格偏好。您可以选择跟风,也可以不跟风。当我遇到类似情况时:
C#:
bool SomeMethod()
{
    :
    if (condition)
        return true;

    return false;
}

我写:
C#:
bool SomeMethod()
{
    :
    return condition;
}
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
对于我来说,查询是否超出每日限额的查询似乎过于复杂,但这可能是一个不同的主题,此外,我不是SQL专家。

无论如何,其余的内容对我来说都很不错(除了空格和编码样式首选项/问题之外)。
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
我可以返回Web.Http.Results而不是HttpResponseMessage吗?任何将响应转换为web.http.results的方法 @跳伞 ?

C#:
var response = new HttpResponseMessage
                {
                    StatusCode = (HttpStatusCode)429,
                    ReasonPhrase = "Invalid Request Time",
                    Content = new StringContent("Requests are permitted between " + WebConfigurationManager.AppSettings["TimeStart"].ToString() + " AM 和 " + WebConfigurationManager.AppSettings["TimeEnd"].ToString() + " PM.")

                };
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
像这样?它给出了错误,没有转换。
C#:
actionContext.Response = new System.Web.Http.Results.ResponseMessageResult(new HttpResponseMessage
               {
                   StatusCode = (HttpStatusCode)429,
                   ReasonPhrase = "Invalid Request Time",
                   Content = new StringContent("Requests are permitted between " + WebConfigurationManager.AppSettings["TimeStart"].ToString() + " AM 和 " + WebConfigurationManager.AppSettings["TimeEnd"].ToString() + " PM."));
               }
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,499
地点
弗吉尼亚州切萨皮克
编程经验
10+
如果您实际上告诉我们您遇到了什么错误,可能会有所帮助...
 
最佳 底部