Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

时间:2022-02-08 19:39:52
 

前言

刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下。有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说起。问题大概有以下几点: 1、项目层的文件夹结构       2、解决MVC的Controller和Web API的Controller类名不能相同的问题       3、给MVC不同命名空间的Area的注册不同的路由       4、让Web API路由配置也支持命名空间参数       5、MVC及Web API添加身份验证及错误处理的过滤器       6、MVC添加自定义参数模型绑定ModelBinder       7、Web API添加自定义参数绑定HttpParameterBinding       8、让Web API同时支持多个Get方法

正文

一、项目层的文件夹结构       这里的结构谈我自己的项目仅供大家参考,不合理的地方欢迎大家指出。第一篇博客中我已经跟大家说了下框架的分层及简单说了下项目层,现在我们再仔细说下。新建MVC或Web API时微软已经给我们创建好了许多的文件夹,如App_Start放全局设置,Content放样式等、Controller放控制器类、Model数据模型、Scripts脚本、Views视图。有些人习惯了传统的三层架构(有些是N层),喜欢把Model文件夹、Controller文件夹等单独一个项目出来,我感觉是没必要,因为在不同文件夹下也算是一种分层了,单独出来最多也就是编译出来的dll是独立的,基本没有太多的区别。所以我还是从简,沿用微软分好的文件夹。先看我的截图       Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

我添加了区域Areas,我的思路是最外层的Model(已删除)、Controllers、Views都只放一些共通的东西,真正的项目放在Areas中,比如上图中Mms代表我的材料管理系统,Psi是另外一个系统,Sys是我的系统管理模块。这样就可以做到多个系统在一个项目中,框架的重用性不言而喻。再具体看区域中一个项目      Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)     这当中微软生成的文件夹只有Controllers、Models、Views。其它都是我建的,比如Common放项目共通的一些类,Reports准备放报表文件、ViewModels放Knouckoutjs的ViewModel脚本文件。      接下来再看看UI库脚本库引入的一些控件要放置在哪里。如下图

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转) 我把框架的css images js themes等都放置在Content下,css中放置项目样式及960gs框架,js下面core是自已定义的一些共通的js包括utils.js、common.js及easyui的knouckout绑定实现knouckout.bindings.js,其它一看就懂基本不用介绍了。

二、解决MVC的Controller和Web API的Controller类名不能相同的问题       回到区域下的一个项目文件夹内,在Controller中我们要创建Mvc Controller及Api Controller,假如一个收料的业务(receive)       mvc路由注册为~/{controller}/{action},我希望的访问地址应该是 ~/receive/action       api中由注册为~/api/{controller},我希望的访问地址应该是 ~/api/receive       那么问题就产生了,微软设计这个框架是通过类名去匹配的 mvc下你创建一个 receiveController继承Controller,就不能再创建一个同名的receiveController继承ApiController,这样的话mvc的访问地址和api的访问地址必须要有一个名字不能叫receive,是不是很郁闷。       通过查看微软System.Web.Http的源码,我们发现其实这个问题也很好解决,在这个DefaultHttpControllerSelector类中,微软有定义Controller的后缀,如图       Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

我们只要把ApiController的后缀改成和MVC不一样,就可以解决问题了。这个字段是个静态只读的Field,我们只要把它改成ApiContrller就解决问题了。我们首先想到的肯定是反射。好吧,就这么做,在注册Api路由前添加以下代码即可完成

var suffix = typeof(DefaultHttpControllerSelector).GetField("ControllerSuffix", BindingFlags.Static | BindingFlags.Public);
if (suffix != null) suffix.SetValue(null, "ApiController");

三、给MVC不同命名空间的Area的注册不同的路由 这个好办,MVC路由配置支持命名空间,新建区域时框架会自动添加{区域名}AreaRegistration.cs文件,用于注册本区域的路由      Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)   在这个文件中的RegisterArea方法中添加以下代码即可

context.MapRoute(
this.AreaName + "default",
this.AreaName + "/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "Zephyr.Areas."+ this.AreaName + ".Controllers" }
);

其中第四个参数是命名空间参数,表示这个路由设置只在此命名空间下有效。

四、让Web API路由配置也支持命名空间参数 让人很头疼的是Web Api路由配置竟然不支持命名空间参数,这间接让我感觉它不支持Area,微软真会开玩笑。好吧我们还是自己动手。在google上找到一篇文章http://netmvc.blogspot.com/2012/06/aspnet-mvc-4-webapi-support-areas-in.html 貌似被墙了,这里有介绍一种方法替换HttpControllerSelector服务。      我直接把我的代码贴出来,大家可以直接用,首先创建一个新的HttpControllerSelector类

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
using System;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Net; namespace Zephyr.Web
{
public class NamespaceHttpControllerSelector : DefaultHttpControllerSelector
{
private const string NamespaceRouteVariableName = "namespaceName";
private readonly HttpConfiguration _configuration;
private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerCache; public NamespaceHttpControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
_configuration = configuration;
_apiControllerCache = new Lazy<ConcurrentDictionary<string, Type>>(
                new Func<ConcurrentDictionary<string, Type>>(InitializeApiControllerCache));
} private ConcurrentDictionary<string, Type> InitializeApiControllerCache()
{
IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver();
var types = this._configuration.Services.GetHttpControllerTypeResolver()
                .GetControllerTypes(assembliesResolver).ToDictionary(t => t.FullName, t => t);

            return new ConcurrentDictionary<string, Type>(types);
} public IEnumerable<string> GetControllerFullName(HttpRequestMessage request, string controllerName)
{
object namespaceName;
var data = request.GetRouteData();
IEnumerable<string> keys = _apiControllerCache.Value.ToDictionary<KeyValuePair<string, Type>, string, Type>(t => t.Key,
t => t.Value, StringComparer.CurrentCultureIgnoreCase).Keys.ToList(); if (!data.Values.TryGetValue(NamespaceRouteVariableName, out namespaceName))
{
return from k in keys
where k.EndsWith(string.Format(".{0}{1}", controllerName,
                       DefaultHttpControllerSelector.ControllerSuffix), StringComparison.CurrentCultureIgnoreCase)
select k;
} string[] namespaces = (string[])namespaceName;
return from n in namespaces
join k in keys on string.Format("{0}.{1}{2}", n, controllerName,
                   DefaultHttpControllerSelector.ControllerSuffix).ToLower() equals k.ToLower()
select k;
} public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
Type type;
if (request == null)
{
throw new ArgumentNullException("request");
}
string controllerName = this.GetControllerName(request);
if (string.IsNullOrEmpty(controllerName))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
string.Format("No route providing a controller name was found to match request URI '{0}'",
                    new object[] { request.RequestUri })));
}
IEnumerable<string> fullNames = GetControllerFullName(request, controllerName);
if (fullNames.Count() == )
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
string.Format("No route providing a controller name was found to match request URI '{0}'",
                        new object[] { request.RequestUri })));
} if (this._apiControllerCache.Value.TryGetValue(fullNames.First(), out type))
{
return new HttpControllerDescriptor(_configuration, controllerName, type);
}
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
string.Format("No route providing a controller name was found to match request URI '{0}'",
                new object[] { request.RequestUri })));
}
}
}
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

然后在WebApiConfig类的Register中替换服务即可实现

config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));

好吧,现在看看如何使用,还是在区域的{AreaName}AreaRegistration类下的RegisterArea方法中注册Api的路由:

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
this.AreaName + "Api",
"api/" + this.AreaName + "/{controller}/{action}/{id}",
new { action = RouteParameter.Optional, id = RouteParameter.Optional,
        namespaceName = new string[] { string.Format("Zephyr.Areas.{0}.Controllers",this.AreaName) } },
new { action = new StartWithConstraint() }
);
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

第三个参数defaults中的namespaceName,上面的服务已实现支持。第四个参数constraints我在第8个问题时会讲到,这里先略过。

五、MVC及Web API添加身份验证及错误处理的过滤器 先说身份验证的问题。无论是mvc还是api都有一个安全性的问题,未通过身份验证的人能不能访问的问题。我们新一个空项目时,默认是没有身份验证的,除非你在控制器类或者方法上面加上Authorize属性才会需要身份验证。但是我的控制器有那么多,我都要给它加上属性,多麻烦,所以我们就想到过滤器了。过滤器中加上后,控制器都不用加就相当于有这个属性了。      Mvc的就直接在FilterConfig类的RegisterGlobalFilters方法中添加以下代码即可

filters.Add(new System.Web.Mvc.AuthorizeAttribute());

Web Api的过滤器没有单独一个配置类,可以写在WebApiConfig类的Register中

config.Filters.Add(new System.Web.Http.AuthorizeAttribute());

Mvc错误处理默认有添加HandleErrorAttribute默认的过滤器,但是我们有可能要捕捉这个错误并记录系统日志那么这个过滤器就不够用了,所以我们要自定义Mvc及Web Api各自的错误处理类,下面贴出我的错误处理,MvcHandleErrorAttribute

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
using System.Web;
using System.Web.Mvc;
using log4net; namespace Zephyr.Web
{
public class MvcHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
ILog log = LogManager.GetLogger(filterContext.RequestContext.HttpContext.Request.Url.LocalPath);
log.Error(filterContext.Exception);
base.OnException(filterContext);
}
}
}
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

Web API的错误处理

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http.Filters;
using log4net; namespace Zephyr.Web
{
public class WebApiExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
ILog log = LogManager.GetLogger(HttpContext.Current.Request.Url.LocalPath);
log.Error(context.Exception); var message = context.Exception.Message;
if (context.Exception.InnerException != null)
message = context.Exception.InnerException.Message; context.Response = new HttpResponseMessage() { Content = new StringContent(message) }; base.OnException(context);
}
}
}
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

然后分别注册到过滤器中,在FilterConfig类的RegisterGlobalFilters方法中

filters.Add(new MvcHandleErrorAttribute());

在WebApiConfig类的Register中

config.Filters.Add(new WebApiExceptionFilter());

这样过滤器就定义好了。

六、MVC添加自定义模型绑定ModelBinder 在MVC中,我们有可能会自定义一些自己想要接收的参数,那么可以通过ModelBinder去实现。比如我要在MVC的方法中接收JObject参数

public JsonResult DoAction(dynamic request)
{ }

直接这样写的话接收到的request为空值,因为JObject这个类型参数Mvc未实现,我们必须自己实现,先新建一个JObjectModelBinder类,添加如下代码实现

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
using System.IO;
using System.Web.Mvc;
using Newtonsoft.Json; namespace Zephyr.Web
{
public class JObjectModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stream = controllerContext.RequestContext.HttpContext.Request.InputStream;
stream.Seek(, SeekOrigin.Begin);
string json = new StreamReader(stream).ReadToEnd();
return JsonConvert.DeserializeObject<dynamic>(json);
}
}
}
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

然后在MVC注册路由后面添加

ModelBinders.Binders.Add(typeof(JObject), new JObjectModelBinder()); //for dynamic model binder

添加之后,在MVC控制器中我们就可以接收JObject参数了。

七、Web API添加自定义参数绑定HttpParameterBinding

不知道微软搞什么鬼,Web Api的参数绑定机制跟Mvc的参数绑定有很大的不同,首先Web Api的绑定机制分两种,一种叫Model Binding,一种叫Formatters,一般情况下Model Binding用于读取query string中的值,而Formatters用于读取body中的值,这个东西要深究还有很多东西,大家有兴趣自己再去研究,我这里就简单说一下如何自定义ModelBinding,比如在Web API中我自己定义了一个叫RequestWrapper的类,我要在Api控制器中接收RequestWrapper的参数,如下

public dynamic Get(RequestWrapper query)
{
//do something
}

那么我们要新建一个RequestWrapperParameterBinding类

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
using System.Collections.Specialized;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using Zephyr.Core; namespace Zephyr.Web
{
public class RequestWrapperParameterBinding : HttpParameterBinding
{
private struct AsyncVoid { }
public RequestWrapperParameterBinding(HttpParameterDescriptor desc) : base(desc) { }
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
            HttpActionContext actionContext, CancellationToken cancellationToken)
{
var request = System.Web.HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query);
var requestWrapper = new RequestWrapper(new NameValueCollection(request));
if (!string.IsNullOrEmpty(request["_xml"]))
{
var xmlType = request["_xml"].Split('.');
var xmlPath = string.Format("~/Views/Shared/Xml/{0}.xml", xmlType[xmlType.Length – ]);
if (xmlType.Length > )
xmlPath = string.Format("~/Areas/{0}/Views/Shared/Xml/{1}.xml", xmlType); requestWrapper.LoadSettingXml(xmlPath);
} SetValue(actionContext, requestWrapper); TaskCompletionSource<AsyncVoid> tcs = new TaskCompletionSource<AsyncVoid>();
tcs.SetResult(default(AsyncVoid));
return tcs.Task;
}
}
}
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

接下来要把这个绑定注册到绑定规则当中,还是在WebApiConfig中添加

config.ParameterBindingRules.Insert(, param => {
if (param.ParameterType == typeof(RequestWrapper))
return new RequestWrapperParameterBinding(param); return null;
});

此时RequestWrapper参数绑定已完成,可以使用了

八、让Web API同时支持多个Get方法 先引用微软官方的东西把存在的问题跟大家说明白,假如Web Api在路由中注册的为

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

然后我的控制器为

public class ProductsController : ApiController
{
public void GetAllProducts() { }
public IEnumerable<Product> GetProductById(int id) { }
public HttpResponseMessage DeleteProduct(int id){ }
}

那么对应的地址请求到的方法如下 Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

看到上面不知道到大家看到问题了没,如果我有两个Get方法(我再加一个GetTop10Products,这种情况很常见),而且参数也相同那么路由就没有办法区分了。有人就想到了修改路由设置,把routeTemplate:修改为"api/{controller}/{action}/{id}",没错,这样是能解决上述问题,但是你的api/products无论是Get Delete Post Input方式都无法请求到对应的方法,你必须要api/products/GetAllProducts、api/products/DeleteProduct/4 ,action名你不能省略。现在明白了问题所在了。我就是要解决这个问题。

还记得我在写第四点的时候有提到这里,思路就是要定义一个constraints去实现:        我们先分析下uri path: api/controller/x,问题就在这里的x,它有可能代表action也有可能代表id,其实我们就是要区分这个x什么情况下代表action什么情况下代表id就可以解决问题了,我是想自己定义一系统的动词,如果你的actoin的名字是以我定义的这些动词中的一个开头,那么我认为你是action,否则认为你是id。

好,思路说明白了,我们开始实现,先定义一个StartWithConstraint类

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Routing; namespace Zephyr.Web
{
/// <summary>
/// 如果请求url如: api/area/controller/x x有可能是actioin或id
/// 在url中的x位置出现的是以 get put delete post开头的字符串,则当作action,否则就当作id
/// 如果action为空,则把请求方法赋给action
/// </summary>
public class StartWithConstraint : IHttpRouteConstraint
{
public string[] array { get; set; }
public bool match { get; set; }
private string _id = "id"; public StartWithConstraint(string[] startwithArray = null)
{
if (startwithArray == null)
startwithArray = new string[] { "GET", "PUT", "DELETE", "POST", "EDIT", "UPDATE", "AUDIT", "DOWNLOAD" }; this.array = startwithArray;
} public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName,
            IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (values == null) // shouldn't ever hit this.
return true; if (!values.ContainsKey(parameterName) || !values.ContainsKey(_id)) // make sure the parameter is there.
return true; var action = values[parameterName].ToString().ToLower();
if (string.IsNullOrEmpty(action)) // if the param key is empty in this case "action" add the method so it doesn't hit other methods like "GetStatus"
{
values[parameterName] = request.Method.ToString();
}
else if (string.IsNullOrEmpty(values[_id].ToString()))
{
var isidstr = true;
array.ToList().ForEach(x =>
{
if (action.StartsWith(x.ToLower()))
isidstr = false;
}); if (isidstr)
{
values[_id] = values[parameterName];
values[parameterName] = request.Method.ToString();
}
}
return true;
}
}
}
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

然后在对应的API路由注册时,添加第四个参数constraints

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
this.AreaName + "Api",
"api/" + this.AreaName + "/{controller}/{action}/{id}",
new { action = RouteParameter.Optional, id = RouteParameter.Optional,
        namespaceName = new string[] { string.Format("Zephyr.Areas.{0}.Controllers",this.AreaName) } },
new { action = new StartWithConstraint() }
);
Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

这样就实现了,Api控制器中Action的取名就要注意点就是了,不过还算是一个比较完美的解决方案。

后述

这次Mvc框架上的问题说的差不多,下次可能就开始写一些具体页面及细节了,如果大家对我的框架有兴趣,我会继续写下去的。不过从前天开始,我已经写了三天的博客了,连续熬了几个晚上到这么晚,我决定我要休息几天了。

转自:http://www.cnblogs.com/xqin/archive/2013/05/31/3109569.html

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)的更多相关文章

  1. Asp&period;Net MVC及Web API框架配置会碰到的几个问题及解决方案

    前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说 ...

  2. 转-Asp&period;Net MVC及Web API框架配置会碰到的几个问题及解决方案

    前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说 ...

  3. 【转】Asp&period;Net MVC及Web API框架配置会碰到的几个问题及解决方案

    前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说 ...

  4. Asp&period;Net MVC及Web API框架配置会碰到的几个问题及解决方案 (精髓)

    前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下.有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文件夹说 ...

  5. ABP示例程序-使用AngularJs,ASP&period;NET MVC,Web API和EntityFramework创建N层的单页面Web应用

    本片文章翻译自ABP在CodeProject上的一个简单示例程序,网站上的程序是用ABP之前的版本创建的,模板创建界面及工程文档有所改变,本文基于最新的模板创建.通过这个简单的示例可以对ABP有个更深 ...

  6. &lbrack;Asp&period;Net&rsqb; MVC 和Web API Action 获取参数的区别

    Asp.net MVC 和web api 的action 在获取从前台传入的数据是有很大不同 前台使用ajax的方式向后台发起post的请求 Content-Type:application/json ...

  7. vs 2013下自定义ASP&period;net MVC 5&sol;Web API 2 模板(T4 视图模板&sol;控制器模板)

    vs 2013下自定义ASP.net MVC 5/Web API 2  模板(T4 视图模板/控制器模板): Customizing ASP.NET MVC 5/Web API 2 Scaffoldi ...

  8. ASP&period;NET MVC&plus;Knockout&plus;Web API&plus;SignalR

    架构设计(ASP.NET MVC+Knockout+Web API+SignalR) 架构设计(ASP.NET MVC+Knockout+Web API+SignalR) 2014-01-16 18: ...

  9. Visual Studio 2013 Preview - ASP&period;NET&comma; MVC 5&comma; Web API 2新功能搶先看

    Visual Studio 2013 Preview - ASP.NET, MVC 5, Web API 2新功能搶先看 來自TechEd North America 2013的第一手消息 以下資訊均 ...

随机推荐

  1. 【转】mysql的union、left join、 right join、 inner join和视图学习

    1.联合 union 进行多个查询语句时,要求多次查询的结果列数必须一样.此时,查询的结果以第一个sql语句的列名为准且union会自动去重复我们应该使用union all. 例...... 1.联合 ...

  2. 横向滑动的GridView

    思路: GridView行数设置为一行,外面套一个HorizontalScrollView,代码中设置GridView宽度 xml代码 <HorizontalScrollView android ...

  3. 备忘:powerbroker运行一个命令

    pbrun su<space>-<space><taget user name> example: pbrun su - pmsdev

  4. 【转】如何确定Kafka的分区数、key和consumer线程数

    文章来源:http://www.cnblogs.com/huxi2b/p/4583249.html -------------------------------------------------- ...

  5. 使用eclipse&plus;tomcat搭建本地环境

    项目开发工具很多,这里简单介绍下使用eclipse+tomcat如何搭建本地环境. 安装开发工具如下: 1. jdk的安装参考 下载地址:http://pan.baidu.com/s/1sj9rVYX ...

  6. gitlab&plus;TortoiseGit中使用SSH

    1.在文件夹空白位置右键打开"Git Bash" 2.按 https://gitlab.yourhost.com/help/ssh/ssh.md 中的说明,输入命令   ssh-k ...

  7. HashMap HashTable HashSet

    原文转载自 http://blog.csdn.net/wl_ldy/article/details/5941770 HashMap是新框架中用来代替HashTable的类 也就是说建议使用HashMa ...

  8. 2019&period;03&period;28 bzoj3598&colon; &lbrack;Scoi2014&rsqb;方伯伯的商场之旅(带权中位数&plus;数位dp)

    传送门 题意咕咕咕自己读吧挺简单的 思路: 由带权中位数的性质可以得到对于每个数放在每个二进制位的代价一定是个单调或者单峰函数,因此我们先把所有的数都挪到第一个位置,然后依次向右枚举峰点(极值点)把能 ...

  9. SQL SERVER 触发器之After,Instead of

    [Ater](同for)先执行增删改操作,再执行触发器操作 [Instead of]直接只执行触发器里的 create trigger triggername on table_name for/af ...

  10. &lbrack;转帖&rsqb;盖国强:Oracle 路线图

    http://www.eygle.com/archives/2018/12/oracle_database_release_19c_confirm.html 盖总的blog 里面讲了下相关的内容. 很 ...