博客 (237)

本文不定时更新中……


具备 RESTful 相关知识会更有利于学习 ASP.NET Web API:RESTful API 学习笔记

ASP.NET Web API 官网:https://www.asp.net/web-api


服务 URI Pattern

ActionHttp verbURI说明
Get contact listGET/api/contacts列表
Get filtered contactsGET/api/contacts?$top=2筛选列表
Get contact by IDGET/api/contacts/id获取一项
Create new contactPOST/api/contacts增加一项
Update a contactPUT/api/contacts/id修改一项
Delete a contactDELETE/api/contacts/id
删除一项


返回类型Web API 创建响应的方式
void返回空 204 (无内容)
HttpResponseMessage转换为直接 HTTP 响应消息。
IHttpActionResult调用 ExecuteAsync 来创建 HttpResponseMessage,然后将转换为 HTTP 响应消息。
其他类型将序列化的返回值写入到响应正文中;返回 200 (正常)。

HttpResponseMessage

可提供大量控制的响应消息。 例如,以下控制器操作设置的缓存控制标头。

public class ValuesController : ApiController
{
    public HttpResponseMessage Get()
    {
        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
        response.Content = new StringContent("hello", Encoding.Unicode);
        response.Headers.CacheControl = new CacheControlHeaderValue()
        {
            MaxAge = TimeSpan.FromMinutes(20)
        };
        return response;
    } 
}

IHttpActionResult

public IHttpActionResult Get (int id)
{
    Product product = _repository.Get (id);
    if (product == null)
    {
        return NotFound(); // Returns a NotFoundResult
    }
    return Ok(product);  // Returns an OkNegotiatedContentResult
}

其他的返回类型

响应状态代码为 200 (正常)。此方法的缺点是,不能直接返回错误代码,如 404。 但是,您可以触发 HttpResponseException 的错误代码


完善 Help 页

一般都是通过元数据注释(Metadata Annotations)来实现的描述的。


显示接口和属性的描述:

打开 HelpPage 区域的 App_Start 目录下的 HelpPageConfig.cs 文件,在 Register 方法的首行取消注释行:

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

在项目右键属性“生成”页,勾选“XML 文档文件”,并填写与上述一致的路径(App_Data/XmlDocument.xml)。


给接口加上 ResponseType 显示响应描述和示例:[ResponseType(typeof(TEntity))]


在实体模型的属性上加入 RequiredAttribute 特性可提供请求或响应中的实体的属性描述,如:[Required]


推荐使用:Swashbuckle

关于格式


在 WebApiConfig.Register() 中加入以下代码以禁止以 XML 格式输出(请按需设置):

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();

Json 序列化去掉 k__BackingField

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver { IgnoreSerializableAttribute = true };

如果属性值为 null 则不序列化该属性

config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };



xoyozo 7 年前
5,173

代码一:

#if DEBUG
    // Debug 模式
#else
    // Release 模式
#endif

作用:判断 调试模式 / 发布模式

条件:须在项目属性的生成页中勾选“定义 DEBUG 常量”

适用:记录 EF 生成的 SQL 语句、控制定时器只在生产环境执行等

注意:调试和发布时应选择正确的模式


代码二:

if(Request.IsLocal) { }

作用:判断 本地 / 远程

条件:在会话中

适用:显示错误详情、记录 EF 生成的 SQL 语句

注意:不能用于定时器、Application_Start、异步操作等非会话中


代码三:

if (System.Net.IPAddress.IsLoopback(HttpContext.Connection.RemoteIpAddress)) { }

说明:ASP.NET Core 中的写法,用于检测 IP 地址是否为本地回环地址(IPv4 为 127.0.0.1,IPv6 为 ::1)

若在视图中使用,HttpContext 改为 ViewContext.HttpContext 或 Context

作用/条件/适用/注意:参“代码二”


代码四:

if(HttpRuntime.AppDomainAppPath == "X:\xxx\") { }

作用:判断当前网站根目录路径

条件:开发环境和生产环境的根目录路径不同

适用:判断根目录路径作相应的处理,适用于以上各种情况

注意:路径有变须要修改相应程序代码

xoyozo 7 年前
6,677

制作类似下图中的拖拽排序功能:

image.png

1. 首先数据库该表中添加字段 sort,类型为 double(MySQL 中为 double(0, 0))。

2. 页面输出绑定数据(以 ASP.NET MVC 控制器为例):

public ActionResult EditSort()
{
    if (!zConsole.Logined) { return RedirectToAction("", "SignIn", new { redirect = Request.Url.OriginalString }); }

    db_auto2018Entities db = new db_auto2018Entities();

    return View(db.dt_dealer.Where(c => c.enabled).OrderBy(c => c.sort));
}

这里可以加条件列出,即示例中 enabled == true 的数据。

3. 前台页面引用 jQuery 和 jQuery UI。

4. 使用 <ul /> 列出数据:

<ul id="sortable" class="list-group gutter list-group-lg list-group-sp">
    @foreach (var d in Model)
    {
        <li class="list-group-item" draggable="true" data-id="@d.id">
            <span class="pull-left"><i class="fa fa-sort text-muted fa m-r-sm"></i> </span>
            <div class="clear">
                【id=@d.id】@d.name_full
            </div>
        </li>
    }
</ul>

5. 初始化 sortable,当拖拽结束时保存次序:

<script>
    var url_SaveSort = '@Url.Action("SaveSort")';
</script>
<script>
    $("#sortable").sortable({
        stop: function (event, ui) {
            // console.log('index: ' + $(ui.item).index())
            // console.log('id: ' + $(ui.item).data('id'))
            // console.log('prev_id: ' + $(ui.item).prev().data('id'))
            $.post(url_SaveSort, {
                id: $(ui.item).data('id'),
                prev_id: $(ui.item).prev().data('id')
            }, function (json) {
                if (json.result.success) {
                    // window.location.reload();
                } else {
                    toastr["error"](json.result.info);
                }
            }, 'json');
        }
    });
    $("#sortable").disableSelection();
</script>

这里回传到服务端的参数为:当前项的 id 值、拖拽后其前面一项的 prev_id 值(若移至首项则 prev_id 为 undefined)。

不使用 $(ui.item).index() 是因为,在有筛选条件的结果集中排序时,使用该索引值配合 LINQ 的 .Skip 会引起取值错误。

6. 控制器接收并保存至数据库:

[HttpPost]
public ActionResult SaveSort(int id, int? prev_id)
{
    if (!zConsole.Logined)
    {
        return Json(new { result = new { success = false, msg = "请登录后重试!" } }, JsonRequestBehavior.AllowGet);
    }

    db_auto2018Entities db = new db_auto2018Entities();

    dt_dealer d = db.dt_dealer.Find(id);

    // 拖拽后其前项 sort 值(若无则 null)(此处不需要加 enabled 等筛选条件)
    double? prev_sort = prev_id.HasValue
        ? db.dt_dealer.Where(c => c.id == prev_id).Select(c => c.sort).Single()
        : null as double?;

    // 拖拽后其后项 sort 值(若无前项则取首项作为后项)(必须强制转化为 double?,否则无后项时会返回 0,导致逻辑错误)
    double? next_sort = prev_id.HasValue
        ? db.dt_dealer.Where(c => c.sort > prev_sort && c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault()
        : db.dt_dealer.Where(c => c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault();

    if (prev_sort.HasValue && next_sort.HasValue)
    {
        d.sort = (prev_sort.Value + next_sort.Value) / 2;
    }
    if (prev_sort == null && next_sort.HasValue)
    {
        d.sort = next_sort.Value - 1;
    }
    if (prev_sort.HasValue && next_sort == null)
    {
        d.sort = prev_sort.Value + 1;
    }

    db.SaveChanges();

    return Json(new { item = new { id = d.id }, result = new { success = true } });
}

需要注意的是,当往数据库添加新项时,必须将 sort 值设置为已存在的最大 sort 值 +1 或最小 sort 值 -1。

var d = new dt_dealer
{
    name_full = "新建项",
    sort = (db.dt_dealer.Max(c => (double?)c.sort) ?? 0) + 1,
};


xoyozo 7 年前
6,851

例:http://api.map.baidu.com/marker?location=纬度,经度&title=标题&content=介绍&output=html

可用于静态图超链接。

xoyozo 7 年前
4,451

当我们初始化一个 Swiper 时,

var mySwiper = new Swiper ('.swiper-container');

如果页面上只有一个 .swiper-container,那么 mySwiper 是一个对象,如果有多个 .swiper-container,则 mySwiper 是一个数组。

处理不当会报以下错误:

Uncaught TypeError: Cannot read property 'appendSlide' of undefined

Uncaught TypeError: Cannot read property 'prependSlide' of undefined

xoyozo 7 年前
4,289

WGS-84(地球坐标)

用于 GPS 模块 及 Google Earth


CGCS 2000(国家大地坐标系)

用于天地图,与 WGS-84 最大差异 0.11mm


GCJ-02(火星坐标、国测局坐标系)

用于 Google Map必应-中国地图高德腾讯图灵阿里地图,是对地理位置的一次加密


BD-09(百度坐标)

仅用于百度地图,是对火星坐标的再次加密


在线转换工具:https://xoyozo.net/Tools/GeoConverter


坐标转换:https://github.com/wandergis/coordtransform



微信网页开发的获取地理位置接口(wx.getLocation)默认使用 wgs84,可选 gcj02。

微信网页开发的使用微信内置地图查看位置接口(wx.openLocation)支持 gcj02。

xoyozo 7 年前
10,789

如果 .NET Core 项目发布到服务器上报以下错误:

HTTP Error 502.5 - Process Failure

Common causes of this issue:

  • The application process failed to start

  • The application process started but then stopped

  • The application process started but failed to listen on the configured port

Troubleshooting steps:

  • Check the system event log for error messages

  • Enable logging the application process' stdout messages

  • Attach a debugger to the application process and inspect

For more information visit: https://go.microsoft.com/fwlink/?LinkID=808681

在服务器上安装最新的 .NET Core Runtime 即可(Windows Server 选择 Hosting Bundle Installer)。

xoyozo 7 年前
5,020

在 Global.asax 的 Application_Start() 方法中添加以下代码,作用是在应用程序启动时预先访问一遍所有动态页面,办法虽土,但很有效。


本代码适合 Web Forms 项目,不适合 MVC 项目;修改代码中的 domain 值为真实的网站域名


代码整理中……


在 IIS 中设置应用程序池最长时效或永不过期,则效果更佳。

在应用程序池中选中网站对应的应用程序池,在右侧“操作”窗口中选择“正在回收...”

取消“固定间隔”框中的所有选项,确定。

未命名-1.png

实践证明,近 200 个动态页面一次性访问需要耗时近 10 分钟,发布后 10 分钟内无法正常浏览网站是同样是无法忍受的。因此更改方案为:

做一个 Winform 应用程序来定时访问这些页面,30 秒一个,一个半小时能完成一个循环,对正常浏览的影响非常小。

xoyozo 8 年前
3,847

函数功能:添加/修改/删除/获取 URL 中的参数(锚点敏感)

/// <summary>
/// 设置 URL 参数(设 value 为 undefined 或 null 删除该参数)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// <param name="value">参数值</param>
/// </summary>
function fn_set_url_param(url, key, value) {
    // 第一步:分离锚点
    var anchor = "";
    var anchor_index = url.indexOf("#");

    if (anchor_index >= 0) {
        anchor = url.substr(anchor_index);
        url = url.substr(0, anchor_index);
    }

    // 第二步:删除参数
    key = encodeURIComponent(key);
    var patt;

    // &key=value
    patt = new RegExp("\\&" + key + "=[^&]*", "gi");
    url = url.replace(patt, "");

    // ?key=value&okey=value  -> ?okey=value
    patt = new RegExp("\\?" + key + "=[^&]*\\&", "gi");
    url = url.replace(patt, "?");

    // ?key=value
    patt = new RegExp("\\?" + key + "=[^&]*$", "gi");
    url = url.replace(patt, "");

    // 第三步:追加参数
    if (value != undefined && value != null) {
        value = encodeURIComponent(value);
        url += (url.indexOf("?") >= 0 ? "&" : "?") + key + "=" + value;
    }

    // 第四步:连接锚点
    url += anchor;

    return url;
}

/// <summary>
/// 获取 URL 参数(无此参数返回 null,多次匹配使用“,”连接)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// </summary>
function fn_get_url_param(url, key) {
    key = encodeURIComponent(key);
    var patt = new RegExp("[?&]" + key + "=([^&#]*)", "gi");

    var values = [];

    while ((result = patt.exec(url)) != null) {
        values.push(decodeURIComponent(result[1]));
    }

    if (values.length > 0) {
        return values.join(",");
    } else {
        return null;
    }
}


调用示例:

var url = window.location.href;

// 设置参数
url = fn_set_url_param(url, 'a', 123);

// 获取参数
console.log(fn_get_url_param, url, 'a');


压缩版:

function fn_set_url_param(b,c,e){var a="";var f=b.indexOf("#");if(f>=0){a=b.substr(f);b=b.substr(0,f)}c=encodeURIComponent(c);var d;d=new RegExp("\\&"+c+"=[^&]*","gi");b=b.replace(d,"");d=new RegExp("\\?"+c+"=[^&]*\\&","gi");b=b.replace(d,"?");d=new RegExp("\\?"+c+"=[^&]*$","gi");b=b.replace(d,"");if(e!=undefined&&e!=null){e=encodeURIComponent(e);b+=(b.indexOf("?")>=0?"&":"?")+c+"="+e}b+=a;return b}
function fn_get_url_param(b,c){c=encodeURIComponent(c);var d=new RegExp("[?&]"+c+"=([^&#]*)","gi");var a=[];while((result=d.exec(b))!=null){a.push(decodeURIComponent(result[1]))}if(a.length>0){return a.join(",")}else{return null}};


xoyozo 8 年前
3,808

风险名称:系统共享配置检测

风险等级:中危


检测项说明:

检测注册表项HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\LSA\\RestrictAnonymous的值,该值控制是否允许远程操作注册表


检测项目:

远程操作注册表

建议值:

1

路径:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA\RestrictAnonymous

建议:

建议禁止远程访问操作注册表,建议关闭


xoyozo 8 年前
4,278